Primary - id: this an unique key comprised of numbers and characters that is assigned to each track generated by Spotify
Numerical - acousticness: range from 0(LOW) to 1(HIGH) - danceability: range from 0(LOW) to 1(HIGH) - energy: range from 0(LOW) to 1(HIGH) - duration_ms: majority range from 200,000 to 300,000 - instrumentalness: range from 0(LOW) to 1(HIGH) - valence: range from 0(LOW) to 1(HIGH) - popularity: range from 0(LOW) to 100(HIGH)* - tempo: majority range from 50(LOW) to 150(high) - liveness: range from 0(LOW) to 1(HIGH) - loudness majority range from -60 to 0 - speechiness: range from 0(LOW) to 1(HIGH) - year: range from 1921 to 2020
Dummy - mode: 0 represents minor and 1 represents major - explicit: 0 represents no explicit content and 1 represents explicit content
Categorical - key: this consists of all different music keys on onctave encoded from 0 to 11 (i.e. C = 0, C# = 1, etc…) - artists: the artist of the track - release_date: the date of release in yyyy-mm-dd format - name: the name of the track
*With our approach on evaluating different tracks, we decided to use popularity as our main dependant variable.
Initial data visualization
Plotting annual median values for each feature overall. Standardized such that each feature is on the same scale.
df <- read.csv("./data/data.csv")
q1s = data.frame(matrix(ncol=3, nrow=0))
medians = data.frame(matrix(ncol=3, nrow=0))
q3s = data.frame(matrix(ncol=3, nrow=0))
feature_names = c("acousticness", "danceability", "instrumentalness", "energy",
"duration_ms", "valence", "tempo", "liveness", "loudness", "speechiness")
for(i in 1921:2020){
annual_val = subset(df, year==i)
for(name in feature_names){
vals = quantile(annual_val[,name])[2:4]
q1s <- rbind(q1s, c(i, name, vals[1]))
medians <- rbind(medians, c(i, name, vals[2]))
q3s <- rbind(q3s, c(i, name, vals[3]))
}
}
colnames(q1s) <-c("Year", "Feature", "Value")
colnames(medians) <-c("Year", "Feature", "Value")
colnames(q3s) <-c("Year", "Feature", "Value")
split_q1s <- split(q1s, q1s$Feature)
split_medians <- split(medians, medians$Feature)
split_q3s <- split(q3s, q3s$Feature)
feature_quantiles = array(0, c(100,3,length(feature_names)))
for(i in 1:length(feature_names)){
for(j in 1:100){
feature_quantiles[j,1,i] = split_q1s[[i]][[3]][j]
feature_quantiles[j,2,i] = split_medians[[i]][[3]][j]
feature_quantiles[j,3,i] = split_q3s[[i]][[3]][j]
}
}
dimnames(feature_quantiles) <- list(1921:2020,
c("Q1", "Median", "Q3"),
feature_names)
plot(1930,1, xlim = c(1921,2020), ylim = range(0,15), xlab="Year", ylab="Range")
for(i in 1:length(feature_names)){
name = feature_names[i]
medians = as.numeric(feature_quantiles[,,name][,"Median"])
avg = mean(medians)
medians = medians / avg
lines(x=1921:2020, y=medians, col=i)
}
legend("topright", lty=1, col=c(1,2,3,4,5,6,7,8,9,10), legend=feature_names)

Plotting annual median values for the feature for the top 10% of songs by popularity. Standardized such that each feature is on the same scale.
q1s = data.frame(matrix(ncol=3, nrow=0))
medians = data.frame(matrix(ncol=3, nrow=0))
q3s = data.frame(matrix(ncol=3, nrow=0))
feature_names = sort(c("acousticness", "danceability", "instrumentalness", "energy",
"duration_ms", "valence", "tempo", "liveness", "loudness", "speechiness"))
for(i in 1921:2020){
annual_val = subset(df, year==i)
top_10 = floor(nrow(annual_val)*0.1)
annual_val = annual_val[order(annual_val$popularity, decreasing=TRUE),]
annual_val = annual_val[1:top_10,]
for(name in feature_names){
vals = quantile(annual_val[,name])[2:4]
q1s <- rbind(q1s, c(i, name, vals[1]))
medians <- rbind(medians, c(i, name, vals[2]))
q3s <- rbind(q3s, c(i, name, vals[3]))
}
}
colnames(q1s) <-c("Year", "Feature", "Value")
colnames(medians) <-c("Year", "Feature", "Value")
colnames(q3s) <-c("Year", "Feature", "Value")
split_q1s <- split(q1s, q1s$Feature)
split_medians <- split(medians, medians$Feature)
split_q3s <- split(q3s, q3s$Feature)
feature_quantiles = array(0, c(100,3,length(feature_names)))
for(i in 1:length(feature_names)){
for(j in 1:100){
feature_quantiles[j,1,i] = split_q1s[[i]][[3]][j]
feature_quantiles[j,2,i] = split_medians[[i]][[3]][j]
feature_quantiles[j,3,i] = split_q3s[[i]][[3]][j]
}
}
dimnames(feature_quantiles) <- list(1921:2020,
c("Q1", "Median", "Q3"),
feature_names)
plot(1930,1, xlim = c(1921,2020), ylim = range(0,17), xlab="Year", ylab="Range")
for(i in 1:length(feature_names)){
name = feature_names[i]
medians = as.numeric(feature_quantiles[,,name][,"Median"])
avg = mean(medians)
medians = medians / avg
lines(x=1921:2020, y=medians, col=i)
}
legend("topright", lty=1, col=c(1,2,3,4,5,6,7,8,9,10), legend=feature_names)

Train time series models to forecast the future models.
# Set up the libraries and the training/testing amounts
library("smooth")
Loading required package: greybox
Package "greybox", v0.6.3 loaded.
If you want to know more about the greybox and forecasting, you can visit my website: https://forecasting.svetunkov.ru/
Attaching package: ‘greybox’
The following object is masked from ‘package:lubridate’:
hm
The following object is masked from ‘package:tidyr’:
spread
This is package "smooth", v2.6.0
Attaching package: ‘smooth’
The following object is masked from ‘package:TTR’:
lags
library("forecast")
library("nnfor")
training.percent = 0.95
nTrain = 100*training.percent
nTest = 100*(1-training.percent)
Accousticness Models
acousticness = ts(as.numeric(feature_quantiles[,"acousticness"]) , start=1921)
acousticness.train = subset(acousticness, start=1, end=nTrain)
acousticness.test = subset(acousticness, start = (nTrain+1), end =(nTrain+nTest))
#ses
acousticness.train.ses <- ses(acousticness.train, h=nTest)
acousticness.ses.mape = mape(acousticness.train.ses$mean, acousticness.test)
plot(acousticness.train.ses, main=paste("Acousticness", "SES"), sub=paste("MAPE:", round(acousticness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(acousticness)

#SARIMA
acousticness.sarima.model <- auto.arima(acousticness.train)
acousticness.sarima <- forecast(acousticness.sarima.model, h=nTest)
acousticness.sarima.mape = mape(acousticness.sarima$mean, acousticness.test)
plot(acousticness.sarima, main=paste("Acousticness", "SARIMA"), sub=paste("MAPE:", round(acousticness.sarima.mape*100,3), "%"), xlab="Year", ylab="Median Value")
lines(acousticness)

#Neural Network
acousticness.train.nn <- elm(acousticness.train)
acousticness.nn.forecast <- forecast(acousticness.train.nn, h=nTest)
acousticness.nn.mape = mape(acousticness.nn.forecast$mean, acousticness.test)
plot(acousticness, main=paste("Acousticness", "NN"), sub=paste("MAPE:", round(acousticness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(acousticness.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future acousticness median values for the overall data set is through SES. However, given that this best MAPE is roughly 37%, this feature cannot be reliably forecast into the future.
Energy models
energy = ts(as.numeric(feature_quantiles[,"energy"]) , start=1921)
energy.train = subset(energy, start=1, end=nTrain)
energy.test = subset(energy, start = (nTrain+1), end =(nTrain+nTest))
#SES
energy.train.ses <- ses(energy.train, h=nTest)
energy.ses.mape = mape(energy.train.ses$mean, energy.test)
plot(energy.train.ses, main=paste("Energy", "SES"), sub=paste("MAPE:", round(energy.ses.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(energy)

#SARIMA
energy.sarima.model <- auto.arima(energy.train)
energy.sarima <- forecast(energy.sarima.model, h=nTest)
energy.sarima.mape = mape(energy.sarima$mean, energy.test)
plot(energy.sarima, main=paste("Energy", "SARIMA"), sub=paste("MAPE:", round(energy.sarima.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(energy)

#Neural Network
energy.train.nn <- elm(energy.train)
energy.nn.forecast <- forecast(energy.train.nn, h=nTest)
energy.nn.mape = mape(energy.nn.forecast$mean, energy.test)
plot(energy, main=paste("Energy", "NN"), sub=paste("MAPE:", round(energy.nn.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(energy.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future energy median values for the overall data set is through SES. Given that this best MAPE is roughly 8%, this feature can be fairly reliably forecast into the future.
Danceability models
danceability = ts(as.numeric(feature_quantiles[,"danceability"]) , start=1921)
danceability.train = subset(danceability, start=1, end=nTrain)
danceability.test = subset(danceability, start = (nTrain+1), end =(nTrain+nTest))
#SES
danceability.train.ses <- ses(danceability.train, h=nTest)
danceability.ses.mape = mape(danceability.train.ses$mean, danceability.test)
plot(danceability.train.ses, main=paste("danceability", "SES"), sub=paste("MAPE:", round(danceability.ses.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(danceability)

#SARIMA
danceability.sarima.model <- auto.arima(danceability.train)
danceability.sarima <- forecast(danceability.sarima.model, h=nTest)
danceability.sarima.mape = mape(danceability.sarima$mean, danceability.test)
plot(danceability.sarima, main=paste("danceability", "SARIMA"), sub=paste("MAPE:", round(danceability.sarima.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(danceability)

#Neural Network
danceability.train.nn <- elm(danceability.train)
danceability.nn.forecast <- forecast(danceability.train.nn, h=nTest)
danceability.nn.mape = mape(danceability.nn.forecast$mean, danceability.test)
plot(danceability, main=paste("danceability", "NN"), sub=paste("MAPE:", round(danceability.nn.mape* 100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(danceability.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future danceability median values for the overall data set is through NN. However, given that this best MAPE is roughly 10.4%, this feature cannot be reliably forecast into the future.
Duration models
duration = ts(as.numeric(feature_quantiles[,"duration_ms"]) , start=1921)
duration.train = subset(duration, start=1, end=nTrain)
duration.test = subset(duration, start = (nTrain+1), end =(nTrain+nTest))
#SES
duration.train.ses <- ses(duration.train, h=nTest)
duration.ses.mape = mape(duration.train.ses$mean, duration.test)
plot(duration.train.ses, main=paste("Duration", "SES"), sub=paste("MAPE:",round(duration.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(duration)

#SARIMA
duration.sarima.model <- auto.arima(duration.train)
duration.sarima <- forecast(duration.sarima.model, h=nTest)
duration.sarima.mape = mape(duration.sarima$mean, duration.test)
plot(duration.sarima, main=paste("Duration", "SARIMA"), sub=paste("MAPE:", round(duration.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(duration)

#Neural Network
duration.train.nn <- elm(duration.train)
duration.nn.forecast <- forecast(duration.train.nn, h=nTest)
duration.nn.mape = mape(duration.nn.forecast$mean, duration.test)
plot(duration, main=paste("Duration", "NN"), sub=paste("MAPE:", round(duration.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(duration.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future duration median values for the overall data set is through NN. Given that this best MAPE is roughly 8%, this feature can be fairly reliably forecast into the future.
Valence
valence = ts(as.numeric(feature_quantiles[,"valence"]) , start=1921)
valence.train = subset(valence, start=1, end=nTrain)
valence.test = subset(valence, start = (nTrain+1), end =(nTrain+nTest))
#SES
valence.train.ses <- ses(valence.train, h=nTest)
valence.ses.mape = mape(valence.train.ses$mean, valence.test)
plot(valence.train.ses, main=paste("valence", "SES"), sub=paste("MAPE:",round(valence.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(valence)

#SARIMA
valence.sarima.model <- auto.arima(valence.train)
valence.sarima <- forecast(valence.sarima.model, h=nTest)
valence.sarima.mape = mape(valence.sarima$mean, valence.test)
plot(valence.sarima, main=paste("valence", "SARIMA"), sub=paste("MAPE:", round(valence.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(valence)

#Neural Network
valence.train.nn <- elm(valence.train)
valence.nn.forecast <- forecast(valence.train.nn, h=nTest)
valence.nn.mape = mape(valence.nn.forecast$mean, valence.test)
plot(valence, main=paste("valence", "NN"), sub=paste("MAPE:", round(valence.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(valence.nn.forecast$mean, start=1921+nTrain), col=2)

Tempo model
tempo = ts(as.numeric(feature_quantiles[,"tempo"]) , start=1921)
tempo.train = subset(tempo, start=1, end=nTrain)
tempo.test = subset(tempo, start = (nTrain+1), end =(nTrain+nTest))
#SES
tempo.train.ses <- ses(tempo.train, h=nTest)
tempo.ses.mape = mape(tempo.train.ses$mean, tempo.test)
plot(tempo.train.ses, main=paste("tempo", "SES"), sub=paste("MAPE:",round(tempo.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(tempo)

#SARIMA
tempo.sarima.model <- auto.arima(tempo.train)
tempo.sarima <- forecast(tempo.sarima.model, h=nTest)
tempo.sarima.mape = mape(tempo.sarima$mean, tempo.test)
plot(tempo.sarima, main=paste("tempo", "SARIMA"), sub=paste("MAPE:", round(tempo.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(tempo)

#Neural Network
tempo.train.nn <- elm(tempo.train)
tempo.nn.forecast <- forecast(tempo.train.nn, h=nTest)
tempo.nn.mape = mape(tempo.nn.forecast$mean, tempo.test)
plot(tempo, main=paste("tempo", "NN"), sub=paste("MAPE:", round(tempo.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(tempo.nn.forecast$mean, start=1921+nTrain), col=2)

Here we see that the best method to predict future tempo median values for the overall data set is through SES. Given that this best MAPE is roughly 1%, this feature can be fairly reliably forecast into the future.
Liveness
liveness = ts(as.numeric(feature_quantiles[,"liveness"]) , start=1921)
liveness.train = subset(liveness, start=1, end=nTrain)
liveness.test = subset(liveness, start = (nTrain+1), end =(nTrain+nTest))
#SES
liveness.train.ses <- ses(liveness.train, h=nTest)
liveness.ses.mape = mape(liveness.train.ses$mean, liveness.test)
plot(liveness.train.ses, main=paste("liveness", "SES"), sub=paste("MAPE:",round(liveness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(liveness)

#SARIMA
liveness.sarima.model <- auto.arima(liveness.train)
liveness.sarima <- forecast(liveness.sarima.model, h=nTest)
liveness.sarima.mape = mape(liveness.sarima$mean, liveness.test)
plot(liveness.sarima, main=paste("liveness", "SARIMA"), sub=paste("MAPE:", round(liveness.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(liveness)

#Neural Network
liveness.train.nn <- elm(liveness.train)
liveness.nn.forecast <- forecast(liveness.train.nn, h=nTest)
liveness.nn.mape = mape(liveness.nn.forecast$mean, liveness.test)
plot(liveness, main=paste("liveness", "NN"), sub=paste("MAPE:", round(liveness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(liveness.nn.forecast$mean, start=1921+nTrain), col=2)

Loudness
loudness = ts(as.numeric(feature_quantiles[,"loudness"]) , start=1921)
loudness.train = subset(loudness, start=1, end=nTrain)
loudness.test = subset(loudness, start = (nTrain+1), end =(nTrain+nTest))
#SES
loudness.train.ses <- ses(loudness.train, h=nTest)
loudness.ses.mape = mape(loudness.train.ses$mean, loudness.test)
plot(loudness.train.ses, main=paste("loudness", "SES"), sub=paste("MAPE:",round(loudness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(loudness)

#SARIMA
loudness.sarima.model <- auto.arima(loudness.train)
loudness.sarima <- forecast(loudness.sarima.model, h=nTest)
loudness.sarima.mape = mape(loudness.sarima$mean, loudness.test)
plot(loudness.sarima, main=paste("loudness", "SARIMA"), sub=paste("MAPE:", round(loudness.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(loudness)

#Neural Network
loudness.train.nn <- elm(loudness.train)
loudness.nn.forecast <- forecast(loudness.train.nn, h=nTest)
loudness.nn.mape = mape(loudness.nn.forecast$mean, loudness.test)
plot(loudness, main=paste("loudness", "NN"), sub=paste("MAPE:", round(loudness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(loudness.nn.forecast$mean, start=1921+nTrain), col=2)

Speechiness
speechiness = ts(as.numeric(feature_quantiles[,"speechiness"]) , start=1921)
speechiness.train = subset(speechiness, start=1, end=nTrain)
speechiness.test = subset(speechiness, start = (nTrain+1), end =(nTrain+nTest))
#SES
speechiness.train.ses <- ses(speechiness.train, h=nTest)
speechiness.ses.mape = mape(speechiness.train.ses$mean, speechiness.test)
plot(speechiness.train.ses, main=paste("speechiness", "SES"), sub=paste("MAPE:",round(speechiness.ses.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(speechiness)

#SARIMA
speechiness.sarima.model <- auto.arima(speechiness.train)
speechiness.sarima <- forecast(speechiness.sarima.model, h=nTest)
speechiness.sarima.mape = mape(speechiness.sarima$mean, speechiness.test)
plot(speechiness.sarima, main=paste("speechiness", "SARIMA"), sub=paste("MAPE:", round(speechiness.sarima.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(speechiness)

#Neural Network
speechiness.train.nn <- elm(speechiness.train)
speechiness.nn.forecast <- forecast(speechiness.train.nn, h=nTest)
speechiness.nn.mape = mape(speechiness.nn.forecast$mean, speechiness.test)
plot(speechiness, main=paste("speechiness", "NN"), sub=paste("MAPE:", round(speechiness.nn.mape*100, 3), "%"), xlab="Year", ylab="Median Value")
lines(ts(speechiness.nn.forecast$mean, start=1921+nTrain), col=2)

Linear Model for Overall Feature Median–>Most Popular Songs’ Feature Median
We will only consider models that had a low enough MAPEs (10% chosen as a threshold) in their best timeseries forecasting model.
Here MAPE is the difference between the actual median for most popular songs and the predicted median.
#Energy
best_energy = ts(as.numeric(best_feature_quantiles[,"energy"]), start=1921, end=2020)
best_energy.train = subset(best_energy, start = 1, end = nTrain)
best_energy.test = subset(best_energy, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_energy.train, overall = energy.train)
best = best_energy.train
overall = energy.train
energy.lm <- lm(best ~ overall)
test_df <- data.frame(overall = energy.test)
best_energy.test.predict <- predict(energy.lm, newdata=test_df, interval="prediction")
best_energy_test_predict.mape <- mape(best_energy.test[1:length(best_energy.test)], best_energy.test.predict[,1])
plot(best_energy,xlab="Year", ylab="Energy", main=paste("Energy Popularity Prediction"), sub = paste("MAPE: ", round(best_energy_test_predict.mape*100, 3), "%"))
lines(ts(best_energy.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Duration
best_duration = ts(as.numeric(best_feature_quantiles[,"duration_ms"]), start=1921, end=2020)
best_duration.train = subset(best_duration, start = 1, end = nTrain)
best_duration.test = subset(best_duration, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_duration.train, overall = duration.train)
best = best_duration.train
overall = duration.train
duration.lm <- lm(best ~ overall)
test_df <- data.frame(overall = duration.test)
best_duration.test.predict <- predict(duration.lm, newdata=test_df, interval="prediction")
best_duration_test.predict.mape <- mape(best_duration.test[1:length(best_duration.test)], best_duration.test.predict[,1])
plot(best_duration,xlab="Year", ylab="duration", main=paste("duration Popularity Prediction"), sub = paste("MAPE: ", round(best_duration_test.predict.mape*100, 3), "%"))
lines(ts(best_duration.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Accousticness
best_acousticness = ts(as.numeric(best_feature_quantiles[,"acousticness"]), start=1921, end=2020)
best_acousticness.train = subset(best_acousticness, start = 1, end = nTrain)
best_acousticness.test = subset(best_acousticness, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_acousticness.train, overall = acousticness.train)
best = best_acousticness.train
overall = acousticness.train
acousticness.lm <- lm(best ~ overall)
test_df <- data.frame(overall = acousticness.test)
best_acousticness.test.predict <- predict(acousticness.lm, newdata=test_df, interval="prediction")
best_acousticness_test.predict.mape <- mape(best_acousticness.test[1:length(best_acousticness.test)], best_acousticness.test.predict[,1])
plot(best_acousticness,xlab="Year", ylab="acousticness", main=paste("acousticness Popularity Prediction"), sub = paste("MAPE: ", round(best_acousticness_test.predict.mape*100, 3), "%"))
lines(ts(best_acousticness.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Valence
best_valence = ts(as.numeric(best_feature_quantiles[,"valence"]), start=1921, end=2020)
best_valence.train = subset(best_valence, start = 1, end = nTrain)
best_valence.test = subset(best_valence, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_valence.train, overall = valence.train)
best = best_valence.train
overall = valence.train
valence.lm <- lm(best ~ overall)
test_df <- data.frame(overall = valence.test)
best_valence.test.predict <- predict(valence.lm, newdata=test_df, interval="prediction")
best_valence_test.predict.mape <- mape(best_valence.test[1:length(best_valence.test)], best_valence.test.predict[,1])
plot(best_valence,xlab="Year", ylab="Valence", main=paste("Valence Popularity Prediction"), sub = paste("MAPE: ", round(best_valence_test.predict.mape*100, 3), "%"))
lines(ts(best_valence.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Tempo
best_tempo = ts(as.numeric(best_feature_quantiles[,"tempo"]), start=1921, end=2020)
best_tempo.train = subset(best_tempo, start = 1, end = nTrain)
best_tempo.test = subset(best_tempo, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_tempo.train, overall = tempo.train)
best = best_tempo.train
overall = tempo.train
tempo.lm <- lm(best ~ overall)
test_df <- data.frame(overall = tempo.test)
best_tempo.test.predict <- predict(tempo.lm, newdata=test_df, interval="prediction")
best_tempo_test.predict.mape <- mape(best_tempo.test[1:length(best_tempo.test)], best_tempo.test.predict[,1])
plot(best_tempo,xlab="Year", ylab="tempo", main=paste("tempo Popularity Prediction"), sub = paste("MAPE: ", round(best_tempo_test.predict.mape*100, 3), "%"))
lines(ts(best_tempo.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Liveness
best_liveness = ts(as.numeric(best_feature_quantiles[,"liveness"]), start=1921, end=2020)
best_liveness.train = subset(best_liveness, start = 1, end = nTrain)
best_liveness.test = subset(best_liveness, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_liveness.train, overall = liveness.train)
best = best_liveness.train
overall = liveness.train
liveness.lm <- lm(best ~ overall)
test_df <- data.frame(overall = liveness.test)
best_liveness.test.predict <- predict(liveness.lm, newdata=test_df, interval="prediction")
best_liveness_test.predict.mape <- mape(best_liveness.test[1:length(best_liveness.test)], best_liveness.test.predict[,1])
plot(best_liveness,xlab="Year", ylab="liveness", main=paste("liveness Popularity Prediction"), sub = paste("MAPE: ", round(best_liveness_test.predict.mape*100, 3), "%"))
lines(ts(best_liveness.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

#Loudness
best_loudness = ts(as.numeric(best_feature_quantiles[,"loudness"]), start=1921, end=2020)
best_loudness.train = subset(best_loudness, start = 1, end = nTrain)
best_loudness.test = subset(best_loudness, start = nTrain+1, end = nTrain+nTest)
train_df = data.frame(best = best_loudness.train, overall = loudness.train)
best = best_loudness.train
overall = loudness.train
loudness.lm <- lm(best ~ overall)
test_df <- data.frame(overall = loudness.test)
best_loudness.test.predict <- predict(loudness.lm, newdata=test_df, interval="prediction")
best_loudness_test.predict.mape <- mape(best_loudness.test[1:length(best_loudness.test)], best_loudness.test.predict[,1])
plot(best_loudness,xlab="Year", ylab="loudness", main=paste("loudness Popularity Prediction"), sub = paste("MAPE: ", round(best_loudness_test.predict.mape*100, 3), "%"))
lines(ts(best_loudness.test.predict[,1], start = 1921+nTrain+1, end = 2020), col = 2)

Now predicting the median values for the most popular songs for this set of features.
The forecast overall median values and linear model predicting the most popular median values for each feature given overall median value will be used to predict the future median values for popular songs for each feature.
Energy
best = best_energy
overall = energy
energy.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = energy.ses$mean)
energy.predict <- predict(energy.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(energy, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's Energy Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(energy.ses$mean, 3), collapse=",") ))
lines(best_energy, col = 2)
lines(ts(energy.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("bottomright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Duration
best = best_duration
overall = duration
duration.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = duration.nn.predict$mean)
duration.predict <- predict(duration.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(duration, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's Duration Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(duration.nn.predict$mean), collapse=",") ))
lines(best_duration, col = 2)
lines(ts(duration.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("topright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Tempo
best = best_tempo
overall = tempo
tempo.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = tempo.ses$mean)
tempo.predict <- predict(tempo.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(tempo, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's Tempo Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(tempo.ses$mean), collapse=",") ))
lines(best_tempo, col = 2)
lines(ts(tempo.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("bottomright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Valence
best = best_valence
overall = valence
valence.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = valence.sarima.predict$mean)
valence.predict <- predict(valence.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(valence, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's valence Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(valence.sarima.predict$mean,3), collapse=",") ))
lines(best_valence, col = 2)
lines(ts(valence.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("bottomright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Liveness
best = best_liveness
overall = liveness
liveness.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = liveness.sarima.predict$mean)
liveness.predict <- predict(liveness.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(liveness, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's Liveness Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(liveness.sarima.predict$mean,3), collapse=",") ))
lines(best_liveness, col = 2)
lines(ts(liveness.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("topright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Loudness
best = best_loudness
overall = loudness
loudness.lm <- lm(best ~ overall)
predict_df <- data.frame(overall = loudness.ses$mean)
loudness.predict <- predict(loudness.lm, newdata=predict_df, interval="prediction")
smallest = min(min(best), min(overall))
biggest = max(max(best), max(overall))
plot(loudness, xlab="Year", ylab="Median Value", ylim=c(smallest, biggest), main="Most Popular Song's Loudness Feature Prediction", sub=paste("Projected median feature value for most popular songs:", paste(round(loudness.ses$mean,1), collapse=",") ))
lines(best_loudness, col = 2)
lines(ts(loudness.predict[,1], start=2020+1, end=2020+nTest), col = 3)
legend("bottomright", lty=1, col=c(1,2,3), legend=c("Overall", "Most Popular", "Most Popular Projection"))

Implications:
Using these values above for the predicted median value for the most popular songs, for each feature, music curators and record labels can predict which “sounds” will be most popular in the future. This information can then be used for future decision making with respect to which artists to sign and which sounds to push.
LS0tCnRpdGxlOiAiU3BvdGlmeSBEYXRhIEFuYWx5c2lzIGFuZCBEZXNjcmlwdGl2ZSBTdGF0aXN0aWNzIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKYXV0aG9yOiBDUyBEdWFscwpkYXRlOiBOb3ZlbWJlciAxOCwgMjAyMAotLS0KCjEuIERhdGEgU2V0IERlc2NyaXB0aW9uCkZvciB0aGlzIHByb2plY3QsIG91ciB0ZWFtIGRlY2lkZWQgdG8gdGFrZSBhIGRhdGEtZHJpdmVuIGFwcHJvYWNoIHRvIGV2YWx1YXRlIG11c2ljLiBXZSBhcmUgdXNpbmcgdGhlIFNwb3RpZnkgRGF0YXNldCBmcm9tIDE5MjEgdG8gMjAyMCB0aGF0IGNvbnNpc3RzIG9mIG92ZXIgMTYwLDAwMCBkaWZmZXJlbnQgdHJhY2tzLiBJbiBvcmRlciB0byBzdGF5IGNvbnNpc3RlbnQgaW4gdGhlIGV2YWx1YXRpb24sIGFsbCBkYXRhIHdhcyBzb3VyY2VkIGZyb20gdGhlIFNwb3RpZnkgV2ViIEFQSS4gVGhlIGZvbGxvd2luZyBzZXQgb2YgZGF0YSBpcyBjb21iaW5hdGlvbiBvZiBQcmltYXJ5LCBOdW1lcmljYWwsIER1bW15KGJpbmFyeSksIGFuZCBDYXRlZ29yaWNhbCBkYXRhLiBUaGlzIGFsbG93cyB1cyB0byBleHBsb3JlIGRpZmZlcmVudCB0eXBlcyBvZiBtb2RlbHMgYW5kIGRyYXcgZGlmZmVyZW50IGluc2lnaHRzLiBIZXJlIGFyZSBhbGwgdGhlIHZhcmlhYmxlcyB0aGF0IGlzIGluY2x1ZGVkIGluIHRoZSBkYXRhIHNldDoKClByaW1hcnkKICAgIC0gaWQ6IHRoaXMgYW4gdW5pcXVlIGtleSBjb21wcmlzZWQgb2YgbnVtYmVycyBhbmQgY2hhcmFjdGVycyB0aGF0IGlzIGFzc2lnbmVkIHRvIGVhY2ggdHJhY2sgZ2VuZXJhdGVkIGJ5IFNwb3RpZnkKCk51bWVyaWNhbAogICAgLSBhY291c3RpY25lc3M6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEoSElHSCkKICAgIC0gZGFuY2VhYmlsaXR5OiByYW5nZSBmcm9tIDAoTE9XKSB0byAxKEhJR0gpCiAgICAtIGVuZXJneTogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSBkdXJhdGlvbl9tczogbWFqb3JpdHkgcmFuZ2UgZnJvbSAyMDAsMDAwIHRvIDMwMCwwMDAKICAgIC0gaW5zdHJ1bWVudGFsbmVzczogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSB2YWxlbmNlOiByYW5nZSBmcm9tIDAoTE9XKSB0byAxKEhJR0gpCiAgICAtIHBvcHVsYXJpdHk6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEwMChISUdIKSoKICAgIC0gdGVtcG86IG1ham9yaXR5IHJhbmdlIGZyb20gNTAoTE9XKSB0byAxNTAoaGlnaCkKICAgIC0gbGl2ZW5lc3M6IHJhbmdlIGZyb20gMChMT1cpIHRvIDEoSElHSCkKICAgIC0gbG91ZG5lc3MgbWFqb3JpdHkgcmFuZ2UgZnJvbSAtNjAgdG8gMAogICAgLSBzcGVlY2hpbmVzczogcmFuZ2UgZnJvbSAwKExPVykgdG8gMShISUdIKQogICAgLSB5ZWFyOiByYW5nZSBmcm9tIDE5MjEgdG8gMjAyMAoKRHVtbXkKICAgIC0gbW9kZTogMCByZXByZXNlbnRzIG1pbm9yIGFuZCAxIHJlcHJlc2VudHMgbWFqb3IKICAgIC0gZXhwbGljaXQ6IDAgcmVwcmVzZW50cyBubyBleHBsaWNpdCBjb250ZW50IGFuZCAxIHJlcHJlc2VudHMgZXhwbGljaXQgY29udGVudAogICAgCkNhdGVnb3JpY2FsCiAgICAtIGtleTogdGhpcyBjb25zaXN0cyBvZiBhbGwgZGlmZmVyZW50IG11c2ljIGtleXMgb24gb25jdGF2ZSBlbmNvZGVkIGZyb20gMCB0byAxMSAoaS5lLiBDID0gMCwgQyMgPSAxLCBldGMuLi4pCiAgICAtIGFydGlzdHM6IHRoZSBhcnRpc3Qgb2YgdGhlIHRyYWNrCiAgICAtIHJlbGVhc2VfZGF0ZTogdGhlIGRhdGUgb2YgcmVsZWFzZSBpbiB5eXl5LW1tLWRkIGZvcm1hdAogICAgLSBuYW1lOiB0aGUgbmFtZSBvZiB0aGUgdHJhY2sKCipXaXRoIG91ciBhcHByb2FjaCBvbiBldmFsdWF0aW5nIGRpZmZlcmVudCB0cmFja3MsIHdlIGRlY2lkZWQgdG8gdXNlIHBvcHVsYXJpdHkgYXMgb3VyIG1haW4gZGVwZW5kYW50IHZhcmlhYmxlLiAKCgpgYGB7cn0Kb3B0aW9ucyhyZXByLm1hdHJpeC5tYXgucm93cz0xMDAsIHJlcHIubWF0cml4Lm1heC5jb2xzPTIwKQpgYGAKCgpJbXBvcnQgYWxsIExpYnJhcmllcwpgYGB7cn0KIyBMaWJyYXJpZXMKb3B0aW9ucyh3YXJuPS0xKQpsaWJyYXJ5KGdncGxvdDIpCmxpYnJhcnkoZHBseXIpCmxpYnJhcnkocGxvdGx5KQpsaWJyYXJ5KGhyYnJ0aGVtZXMpCmxpYnJhcnkoZm9yZWNhc3QpCmxpYnJhcnkoeHRzKQpsaWJyYXJ5KE1ldHJpY3MpCmxpYnJhcnkocHN5Y2gpCmxpYnJhcnkoZHlncmFwaHMpCmxpYnJhcnkoR0dhbGx5KQpsaWJyYXJ5KHRpZHl2ZXJzZSkKbGlicmFyeSh0aWR5cXVhbnQpICAKbGlicmFyeShjcmFubG9ncykgICAKbGlicmFyeShjb3JycikgICAgICAKbGlicmFyeShjb3dwbG90KSAgCmxpYnJhcnkoTWV0cmljcykKYGBgCiMgSW5pdGlhbCBkYXRhIHZpc3VhbGl6YXRpb24KIyMgUGxvdHRpbmcgYW5udWFsIG1lZGlhbiB2YWx1ZXMgZm9yIGVhY2ggZmVhdHVyZSBvdmVyYWxsLiBTdGFuZGFyZGl6ZWQgc3VjaCB0aGF0IGVhY2ggZmVhdHVyZSBpcyBvbiB0aGUgc2FtZSBzY2FsZS4KYGBge3J9CmRmIDwtIHJlYWQuY3N2KCIuL2RhdGEvZGF0YS5jc3YiKQpxMXMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCm1lZGlhbnMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCnEzcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKZmVhdHVyZV9uYW1lcyA9IGMoImFjb3VzdGljbmVzcyIsICJkYW5jZWFiaWxpdHkiLCAiaW5zdHJ1bWVudGFsbmVzcyIsICJlbmVyZ3kiLAogICAgICAgICAgICAgICAgICAiZHVyYXRpb25fbXMiLCAidmFsZW5jZSIsICJ0ZW1wbyIsICJsaXZlbmVzcyIsICJsb3VkbmVzcyIsICJzcGVlY2hpbmVzcyIpCmZvcihpIGluIDE5MjE6MjAyMCl7CiAgYW5udWFsX3ZhbCA9IHN1YnNldChkZiwgeWVhcj09aSkKCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIHExcyA8LSByYmluZChxMXMsIGMoaSwgbmFtZSwgdmFsc1sxXSkpCiAgICBtZWRpYW5zIDwtIHJiaW5kKG1lZGlhbnMsIGMoaSwgbmFtZSwgdmFsc1syXSkpCiAgICBxM3MgPC0gcmJpbmQocTNzLCBjKGksIG5hbWUsIHZhbHNbM10pKQogIH0KfQoKY29sbmFtZXMocTFzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMocTNzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCgpzcGxpdF9xMXMgPC0gc3BsaXQocTFzLCBxMXMkRmVhdHVyZSkKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X3EzcyA8LSBzcGxpdChxM3MsIHEzcyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBhcnJheSgwLCBjKDEwMCwzLGxlbmd0aChmZWF0dXJlX25hbWVzKSkpCgpmb3IoaSBpbiAxOmxlbmd0aChmZWF0dXJlX25hbWVzKSl7CiAgZm9yKGogaW4gMToxMDApewogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwxLGldID0gc3BsaXRfcTFzW1tpXV1bWzNdXVtqXQogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwyLGldID0gc3BsaXRfbWVkaWFuc1tbaV1dW1szXV1bal0KICAgIGZlYXR1cmVfcXVhbnRpbGVzW2osMyxpXSA9IHNwbGl0X3Ezc1tbaV1dW1szXV1bal0KICB9Cn0KCmRpbW5hbWVzKGZlYXR1cmVfcXVhbnRpbGVzKSA8LSBsaXN0KDE5MjE6MjAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiUTEiLCAiTWVkaWFuIiwgIlEzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCgoKCnBsb3QoMTkzMCwxLCB4bGltID0gYygxOTIxLDIwMjApLCB5bGltID0gcmFuZ2UoMCwxNSksIHhsYWI9IlllYXIiLCB5bGFiPSJSYW5nZSIpCmZvcihpIGluIDE6bGVuZ3RoKGZlYXR1cmVfbmFtZXMpKXsKICBuYW1lID0gZmVhdHVyZV9uYW1lc1tpXQogIG1lZGlhbnMgPSBhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywsbmFtZV1bLCJNZWRpYW4iXSkKCiAgYXZnID0gbWVhbihtZWRpYW5zKQogIG1lZGlhbnMgPSBtZWRpYW5zIC8gYXZnCiAgCiAgbGluZXMoeD0xOTIxOjIwMjAsIHk9bWVkaWFucywgY29sPWkpCn0KbGVnZW5kKCJ0b3ByaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyw0LDUsNiw3LDgsOSwxMCksIGxlZ2VuZD1mZWF0dXJlX25hbWVzKQpgYGAKCgojIyBQbG90dGluZyBhbm51YWwgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIGZlYXR1cmUgZm9yIHRoZSAqdG9wIDEwJSBvZiBzb25ncyogYnkgcG9wdWxhcml0eS4gU3RhbmRhcmRpemVkIHN1Y2ggdGhhdCBlYWNoIGZlYXR1cmUgaXMgb24gdGhlIHNhbWUgc2NhbGUuCmBgYHtyfQpxMXMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCm1lZGlhbnMgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCnEzcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKZmVhdHVyZV9uYW1lcyA9IHNvcnQoYygiYWNvdXN0aWNuZXNzIiwgImRhbmNlYWJpbGl0eSIsICJpbnN0cnVtZW50YWxuZXNzIiwgImVuZXJneSIsCiAgICAgICAgICAgICAgICAgICJkdXJhdGlvbl9tcyIsICJ2YWxlbmNlIiwgInRlbXBvIiwgImxpdmVuZXNzIiwgImxvdWRuZXNzIiwgInNwZWVjaGluZXNzIikpCmZvcihpIGluIDE5MjE6MjAyMCl7CiAgYW5udWFsX3ZhbCA9IHN1YnNldChkZiwgeWVhcj09aSkKICAKICAKICB0b3BfMTAgPSBmbG9vcihucm93KGFubnVhbF92YWwpKjAuMSkKICBhbm51YWxfdmFsID0gYW5udWFsX3ZhbFtvcmRlcihhbm51YWxfdmFsJHBvcHVsYXJpdHksIGRlY3JlYXNpbmc9VFJVRSksXQogIGFubnVhbF92YWwgPSBhbm51YWxfdmFsWzE6dG9wXzEwLF0KCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIHExcyA8LSByYmluZChxMXMsIGMoaSwgbmFtZSwgdmFsc1sxXSkpCiAgICBtZWRpYW5zIDwtIHJiaW5kKG1lZGlhbnMsIGMoaSwgbmFtZSwgdmFsc1syXSkpCiAgICBxM3MgPC0gcmJpbmQocTNzLCBjKGksIG5hbWUsIHZhbHNbM10pKQogIH0KfQoKY29sbmFtZXMocTFzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMocTNzKSA8LWMoIlllYXIiLCAiRmVhdHVyZSIsICJWYWx1ZSIpCgpzcGxpdF9xMXMgPC0gc3BsaXQocTFzLCBxMXMkRmVhdHVyZSkKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X3EzcyA8LSBzcGxpdChxM3MsIHEzcyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBhcnJheSgwLCBjKDEwMCwzLGxlbmd0aChmZWF0dXJlX25hbWVzKSkpCgpmb3IoaSBpbiAxOmxlbmd0aChmZWF0dXJlX25hbWVzKSl7CiAgZm9yKGogaW4gMToxMDApewogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwxLGldID0gc3BsaXRfcTFzW1tpXV1bWzNdXVtqXQogICAgZmVhdHVyZV9xdWFudGlsZXNbaiwyLGldID0gc3BsaXRfbWVkaWFuc1tbaV1dW1szXV1bal0KICAgIGZlYXR1cmVfcXVhbnRpbGVzW2osMyxpXSA9IHNwbGl0X3Ezc1tbaV1dW1szXV1bal0KICB9Cn0KCmRpbW5hbWVzKGZlYXR1cmVfcXVhbnRpbGVzKSA8LSBsaXN0KDE5MjE6MjAyMCwKICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgYygiUTEiLCAiTWVkaWFuIiwgIlEzIiksCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCgoKcGxvdCgxOTMwLDEsIHhsaW0gPSBjKDE5MjEsMjAyMCksIHlsaW0gPSByYW5nZSgwLDE3KSwgeGxhYj0iWWVhciIsIHlsYWI9IlJhbmdlIikKZm9yKGkgaW4gMTpsZW5ndGgoZmVhdHVyZV9uYW1lcykpewogIG5hbWUgPSBmZWF0dXJlX25hbWVzW2ldCiAgbWVkaWFucyA9IGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCxuYW1lXVssIk1lZGlhbiJdKQogIGF2ZyA9IG1lYW4obWVkaWFucykKICBtZWRpYW5zID0gbWVkaWFucyAvIGF2ZwogIAogIGxpbmVzKHg9MTkyMToyMDIwLCB5PW1lZGlhbnMsIGNvbD1pKQp9CmxlZ2VuZCgidG9wcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyLDMsNCw1LDYsNyw4LDksMTApLCBsZWdlbmQ9ZmVhdHVyZV9uYW1lcykKYGBgCgojIyBOb3cgcGxvdHRpbmcgYWJzb2x1dGUgdmFsdWVzIGZvciBlYWNoIGZlYXR1cmUgYXMgdGhlIG1lZGlhbiB2YWx1ZXMgZm9yIG92ZXJhbGwsIGFzIHdlbGwgYXMganVzdCB0aGUgbW9zdCBwb3B1bGFyIHNvbmdzLgojIyMgTUFQRSBpcyB0aGUgZGlmZmVyZW5jZSBiZXR3ZWVuIHRoZSBtb3N0IHBvcHVsYXIgbWVkaWFuIGFuZCB0aGUgb3ZlcmFsbCBtZWRpYW47IFIgaXMgdGhlIGNvcnJlbGF0aW9uIGJldHdlZW4gb3ZlcmFsbCBtZWRpYW4gYW5kIG1vc3QgcG9wdWxhciBtZWRpYW4uCiMjIyBUaGVzZSB2YWx1ZXMgd2lsbCBkZXRlcm1pbmUgd2hldGhlciBvciBub3QgdGhlIG92ZXJhbGwgbWVkaWFuIGlzIGEgdXNlZnVsIHByZWRpY3RvciBvZiB3aGF0IHRoZSBtZWRpYW4gZm9yIHBvcHVsYXIgc29uZ3Mgd2lsbCBiZS4gVGhpcyBpcyBpbXBvcnRhbnQgYmVjYXVzZSBpdCB3aWxsIGJlIGVhc2llciB0byBmb3JlY2FzdCBvdmVyYWxsIHZhbHVlcyBpbnRvIHRoZSBmdXR1cmUsIGdpdmVuIHRoZXkgYXJlIG11Y2ggc21vb3RoZXIsIGFuZCB0aGVzZSBmb3JlY2FzdCBvdmVyYWxsIHZhbHVlcyBjYW4gYmUgdXNlZCB0byBwcmVkaWN0IHRoZSBtb3N0IHBvcHVsYXIgZmVhdHVyZSB2YWx1ZS4KYGBge3J9CiMgR2V0IHRoZSBsaXN0IG9mIGZlYXR1cmVzIEkgbmVlZCB0byB3b3JrIHdpdGgKCmxpYnJhcnkoTWV0cmljcykKCnExcyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKbWVkaWFucyA9IGRhdGEuZnJhbWUobWF0cml4KG5jb2w9MywgbnJvdz0wKSkKYmVzdF9tZWRpYW5zID0gZGF0YS5mcmFtZShtYXRyaXgobmNvbD0zLCBucm93PTApKQpxM3MgPSBkYXRhLmZyYW1lKG1hdHJpeChuY29sPTMsIG5yb3c9MCkpCmZlYXR1cmVfbmFtZXMgPSBzb3J0KGMoImFjb3VzdGljbmVzcyIsICJkYW5jZWFiaWxpdHkiLCAiaW5zdHJ1bWVudGFsbmVzcyIsICJlbmVyZ3kiLAogICAgICAgICAgICAgICAgICAiZHVyYXRpb25fbXMiLCAidmFsZW5jZSIsICJ0ZW1wbyIsICJsaXZlbmVzcyIsICJsb3VkbmVzcyIsICJzcGVlY2hpbmVzcyIpKQpmb3IoaSBpbiAxOTIxOjIwMjApewogIGFubnVhbF92YWwgPSBzdWJzZXQoZGYsIHllYXI9PWkpCiAgdG9wXzEwID0gZmxvb3IobnJvdyhhbm51YWxfdmFsKSowLjEpCiAgYW5udWFsX3ZhbCA9IGFubnVhbF92YWxbb3JkZXIoYW5udWFsX3ZhbCRwb3B1bGFyaXR5LCBkZWNyZWFzaW5nPVRSVUUpLF0KICBiZXN0X29uZXMgPSBhbm51YWxfdmFsWzE6dG9wXzEwLF0KCiAgZm9yKG5hbWUgaW4gZmVhdHVyZV9uYW1lcyl7CiAgICB2YWxzID0gcXVhbnRpbGUoYW5udWFsX3ZhbFssbmFtZV0pWzI6NF0KICAgIGJlc3RfdmFscyA9IHF1YW50aWxlKGJlc3Rfb25lc1ssbmFtZV0pWzI6NF0KICAgIGJlc3RfbWVkaWFucyA8LSByYmluZChiZXN0X21lZGlhbnMsIGMoaSwgbmFtZSwgYmVzdF92YWxzWzJdKSkKICAgIG1lZGlhbnMgPC0gcmJpbmQobWVkaWFucywgYyhpLCBuYW1lLCB2YWxzWzJdKSkKICB9Cn0KCmNvbG5hbWVzKG1lZGlhbnMpIDwtYygiWWVhciIsICJGZWF0dXJlIiwgIlZhbHVlIikKY29sbmFtZXMoYmVzdF9tZWRpYW5zKSA8LSBjKCJZZWFyIiwgIkZlYXR1cmUiLCAiVmFsdWUiKQoKc3BsaXRfbWVkaWFucyA8LSBzcGxpdChtZWRpYW5zLCBtZWRpYW5zJEZlYXR1cmUpCnNwbGl0X2Jlc3RfbWVkaWFucyA8LSBzcGxpdChiZXN0X21lZGlhbnMsIGJlc3RfbWVkaWFucyRGZWF0dXJlKQoKZmVhdHVyZV9xdWFudGlsZXMgPSBtYXRyaXgoMCwgbmNvbD1sZW5ndGgoZmVhdHVyZV9uYW1lcyksIG5yb3c9MTAwKQpiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzID0gbWF0cml4KDAsIG5jb2w9bGVuZ3RoKGZlYXR1cmVfbmFtZXMpLCBucm93PTEwMCkKCmZvcihpIGluIDE6bGVuZ3RoKGZlYXR1cmVfbmFtZXMpKXsKICBmb3IoaiBpbiAxOjEwMCl7CiAgICBmZWF0dXJlX3F1YW50aWxlc1tqLGldIDwtIHNwbGl0X21lZGlhbnNbW2ldXVtbM11dW2pdCiAgICBiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzW2osaV0gPSBzcGxpdF9iZXN0X21lZGlhbnNbW2ldXVtbM11dW2pdCiAgfQp9CgpkaW1uYW1lcyhmZWF0dXJlX3F1YW50aWxlcykgPC0gbGlzdCgxOTIxOjIwMjAsCiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIGZlYXR1cmVfbmFtZXMpCmRpbW5hbWVzKGJlc3RfZmVhdHVyZV9xdWFudGlsZXMpIDwtIGxpc3QoMTkyMToyMDIwLAogICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBmZWF0dXJlX25hbWVzKQoKCgoKZm9yKGkgaW4gMTpsZW5ndGgoZmVhdHVyZV9uYW1lcykpewogIG5hbWUgPSBmZWF0dXJlX25hbWVzW2ldCiAgc2NvcmVzID0gYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssaV0pCiAgYmVzdF9zY29yZXMgPSBhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLGldKQogIG1hcGUgPSBtYXBlKGJlc3Rfc2NvcmVzLCBzY29yZXMpCiAgbWFwZSA9IHJvdW5kKG1hcGUgKiAxMDAsMykKICByICA9IGNvcihzY29yZXMsIGJlc3Rfc2NvcmVzKQogIHB2YWwgPSB0LnRlc3Qoc2NvcmVzLCApCiAgIyBOb3JtYWxpemUgaXQ/Pz8/PyAvIG1ha2UgaXQgYSBmdW5jdGlvbiBvZiBpdHMgb3duIG1lYW4KICAKICBzbWFsbGVzdCA9IG1pbihtaW4oc2NvcmVzKSwgbWluKGJlc3Rfc2NvcmVzKSkKICBiaWdnZXN0ID0gbWF4KG1heChzY29yZXMpLCBtYXgoYmVzdF9zY29yZXMpKQogIAogIHBsb3QoeD0xOTIxOjIwMjAsIHk9c2NvcmVzLCBjb2w9MSwgdHlwZT0ibCIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49bmFtZSwgc3ViPXBhc3RlKCJNQVBFOiAiLCBtYXBlLCAiJTsiLCAiUjoiLCByKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCiAgbGluZXMoeD0xOTIxOjIwMjAsIHk9YmVzdF9zY29yZXMsIGNvbD0yKQogIGxlZ2VuZCgidG9wcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIikpCiAgdGl0bGUoKQp9CmBgYAoKCgojIFRyYWluIHRpbWUgc2VyaWVzIG1vZGVscyB0byBmb3JlY2FzdCB0aGUgZnV0dXJlIG1vZGVscy4KYGBge3J9CiMgU2V0IHVwIHRoZSBsaWJyYXJpZXMgYW5kIHRoZSB0cmFpbmluZy90ZXN0aW5nIGFtb3VudHMKbGlicmFyeSgic21vb3RoIikKbGlicmFyeSgiZm9yZWNhc3QiKQpsaWJyYXJ5KCJubmZvciIpCnRyYWluaW5nLnBlcmNlbnQgPSAwLjk1Cm5UcmFpbiA9IDEwMCp0cmFpbmluZy5wZXJjZW50Cm5UZXN0ID0gMTAwKigxLXRyYWluaW5nLnBlcmNlbnQpCmBgYAoKIyMgQWNjb3VzdGljbmVzcyBNb2RlbHMKCmBgYHtyfQphY291c3RpY25lc3MgPSB0cyhhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywiYWNvdXN0aWNuZXNzIl0pICwgc3RhcnQ9MTkyMSkKYWNvdXN0aWNuZXNzLnRyYWluID0gc3Vic2V0KGFjb3VzdGljbmVzcywgc3RhcnQ9MSwgZW5kPW5UcmFpbikKYWNvdXN0aWNuZXNzLnRlc3QgPSBzdWJzZXQoYWNvdXN0aWNuZXNzLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojc2VzCmFjb3VzdGljbmVzcy50cmFpbi5zZXMgPC0gc2VzKGFjb3VzdGljbmVzcy50cmFpbiwgaD1uVGVzdCkKYWNvdXN0aWNuZXNzLnNlcy5tYXBlID0gbWFwZShhY291c3RpY25lc3MudHJhaW4uc2VzJG1lYW4sIGFjb3VzdGljbmVzcy50ZXN0KQpwbG90KGFjb3VzdGljbmVzcy50cmFpbi5zZXMsIG1haW49cGFzdGUoIkFjb3VzdGljbmVzcyIsICJTRVMiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGFjb3VzdGljbmVzcy5zZXMubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoYWNvdXN0aWNuZXNzKQoKI1NBUklNQQphY291c3RpY25lc3Muc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEoYWNvdXN0aWNuZXNzLnRyYWluKQphY291c3RpY25lc3Muc2FyaW1hIDwtIGZvcmVjYXN0KGFjb3VzdGljbmVzcy5zYXJpbWEubW9kZWwsIGg9blRlc3QpCmFjb3VzdGljbmVzcy5zYXJpbWEubWFwZSA9IG1hcGUoYWNvdXN0aWNuZXNzLnNhcmltYSRtZWFuLCBhY291c3RpY25lc3MudGVzdCkKcGxvdChhY291c3RpY25lc3Muc2FyaW1hLCBtYWluPXBhc3RlKCJBY291c3RpY25lc3MiLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChhY291c3RpY25lc3Muc2FyaW1hLm1hcGUqMTAwLDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoYWNvdXN0aWNuZXNzKQoKI05ldXJhbCBOZXR3b3JrCmFjb3VzdGljbmVzcy50cmFpbi5ubiA8LSBlbG0oYWNvdXN0aWNuZXNzLnRyYWluKQphY291c3RpY25lc3Mubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QoYWNvdXN0aWNuZXNzLnRyYWluLm5uLCBoPW5UZXN0KQphY291c3RpY25lc3Mubm4ubWFwZSA9IG1hcGUoYWNvdXN0aWNuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIGFjb3VzdGljbmVzcy50ZXN0KQpwbG90KGFjb3VzdGljbmVzcywgbWFpbj1wYXN0ZSgiQWNvdXN0aWNuZXNzIiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChhY291c3RpY25lc3Mubm4ubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModHMoYWNvdXN0aWNuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYApIZXJlIHdlIHNlZSB0aGF0IHRoZSBiZXN0IG1ldGhvZCB0byBwcmVkaWN0IGZ1dHVyZSBhY291c3RpY25lc3MgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBTRVMuIEhvd2V2ZXIsIGdpdmVuIHRoYXQgdGhpcyBiZXN0IE1BUEUgaXMgcm91Z2hseSAzNyUsIHRoaXMgZmVhdHVyZSBjYW5ub3QgYmUgcmVsaWFibHkgZm9yZWNhc3QgaW50byB0aGUgZnV0dXJlLgogIAoKCgojIyBFbmVyZ3kgbW9kZWxzCmBgYHtyfQplbmVyZ3kgPSB0cyhhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywiZW5lcmd5Il0pICwgc3RhcnQ9MTkyMSkKZW5lcmd5LnRyYWluID0gc3Vic2V0KGVuZXJneSwgc3RhcnQ9MSwgZW5kPW5UcmFpbikKZW5lcmd5LnRlc3QgPSBzdWJzZXQoZW5lcmd5LCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCmVuZXJneS50cmFpbi5zZXMgPC0gc2VzKGVuZXJneS50cmFpbiwgaD1uVGVzdCkKZW5lcmd5LnNlcy5tYXBlID0gbWFwZShlbmVyZ3kudHJhaW4uc2VzJG1lYW4sIGVuZXJneS50ZXN0KQpwbG90KGVuZXJneS50cmFpbi5zZXMsIG1haW49cGFzdGUoIkVuZXJneSIsICJTRVMiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGVuZXJneS5zZXMubWFwZSogMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGVuZXJneSkKCiNTQVJJTUEKZW5lcmd5LnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKGVuZXJneS50cmFpbikKZW5lcmd5LnNhcmltYSA8LSBmb3JlY2FzdChlbmVyZ3kuc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQplbmVyZ3kuc2FyaW1hLm1hcGUgPSBtYXBlKGVuZXJneS5zYXJpbWEkbWVhbiwgZW5lcmd5LnRlc3QpCnBsb3QoZW5lcmd5LnNhcmltYSwgbWFpbj1wYXN0ZSgiRW5lcmd5IiwgIlNBUklNQSIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZW5lcmd5LnNhcmltYS5tYXBlKiAxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoZW5lcmd5KQoKI05ldXJhbCBOZXR3b3JrCmVuZXJneS50cmFpbi5ubiA8LSBlbG0oZW5lcmd5LnRyYWluKQplbmVyZ3kubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QoZW5lcmd5LnRyYWluLm5uLCBoPW5UZXN0KQplbmVyZ3kubm4ubWFwZSA9IG1hcGUoZW5lcmd5Lm5uLmZvcmVjYXN0JG1lYW4sIGVuZXJneS50ZXN0KQpwbG90KGVuZXJneSwgbWFpbj1wYXN0ZSgiRW5lcmd5IiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChlbmVyZ3kubm4ubWFwZSogMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKGVuZXJneS5ubi5mb3JlY2FzdCRtZWFuLCBzdGFydD0xOTIxK25UcmFpbiksIGNvbD0yKQpgYGAKSGVyZSB3ZSBzZWUgdGhhdCB0aGUgYmVzdCBtZXRob2QgdG8gcHJlZGljdCBmdXR1cmUgZW5lcmd5IG1lZGlhbiB2YWx1ZXMgZm9yIHRoZSBvdmVyYWxsIGRhdGEgc2V0IGlzIHRocm91Z2ggU0VTLiBHaXZlbiB0aGF0IHRoaXMgYmVzdCBNQVBFIGlzIHJvdWdobHkgOCUsIHRoaXMgZmVhdHVyZSBjYW4gYmUgZmFpcmx5IHJlbGlhYmx5IGZvcmVjYXN0IGludG8gdGhlIGZ1dHVyZS4KICAKCiMjIERhbmNlYWJpbGl0eSBtb2RlbHMKYGBge3J9CmRhbmNlYWJpbGl0eSA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJkYW5jZWFiaWxpdHkiXSkgLCBzdGFydD0xOTIxKQpkYW5jZWFiaWxpdHkudHJhaW4gPSBzdWJzZXQoZGFuY2VhYmlsaXR5LCBzdGFydD0xLCBlbmQ9blRyYWluKQpkYW5jZWFiaWxpdHkudGVzdCA9IHN1YnNldChkYW5jZWFiaWxpdHksIHN0YXJ0ID0gKG5UcmFpbisxKSwgZW5kID0oblRyYWluK25UZXN0KSkKCiNTRVMKZGFuY2VhYmlsaXR5LnRyYWluLnNlcyA8LSBzZXMoZGFuY2VhYmlsaXR5LnRyYWluLCBoPW5UZXN0KQpkYW5jZWFiaWxpdHkuc2VzLm1hcGUgPSBtYXBlKGRhbmNlYWJpbGl0eS50cmFpbi5zZXMkbWVhbiwgZGFuY2VhYmlsaXR5LnRlc3QpCnBsb3QoZGFuY2VhYmlsaXR5LnRyYWluLnNlcywgbWFpbj1wYXN0ZSgiZGFuY2VhYmlsaXR5IiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZGFuY2VhYmlsaXR5LnNlcy5tYXBlKiAxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMoZGFuY2VhYmlsaXR5KQoKI1NBUklNQQpkYW5jZWFiaWxpdHkuc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEoZGFuY2VhYmlsaXR5LnRyYWluKQpkYW5jZWFiaWxpdHkuc2FyaW1hIDwtIGZvcmVjYXN0KGRhbmNlYWJpbGl0eS5zYXJpbWEubW9kZWwsIGg9blRlc3QpCmRhbmNlYWJpbGl0eS5zYXJpbWEubWFwZSA9IG1hcGUoZGFuY2VhYmlsaXR5LnNhcmltYSRtZWFuLCBkYW5jZWFiaWxpdHkudGVzdCkKcGxvdChkYW5jZWFiaWxpdHkuc2FyaW1hLCBtYWluPXBhc3RlKCJkYW5jZWFiaWxpdHkiLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChkYW5jZWFiaWxpdHkuc2FyaW1hLm1hcGUqIDEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhkYW5jZWFiaWxpdHkpCgojTmV1cmFsIE5ldHdvcmsKZGFuY2VhYmlsaXR5LnRyYWluLm5uIDwtIGVsbShkYW5jZWFiaWxpdHkudHJhaW4pCmRhbmNlYWJpbGl0eS5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChkYW5jZWFiaWxpdHkudHJhaW4ubm4sIGg9blRlc3QpCmRhbmNlYWJpbGl0eS5ubi5tYXBlID0gbWFwZShkYW5jZWFiaWxpdHkubm4uZm9yZWNhc3QkbWVhbiwgZGFuY2VhYmlsaXR5LnRlc3QpCnBsb3QoZGFuY2VhYmlsaXR5LCBtYWluPXBhc3RlKCJkYW5jZWFiaWxpdHkiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGRhbmNlYWJpbGl0eS5ubi5tYXBlKiAxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModHMoZGFuY2VhYmlsaXR5Lm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYApIZXJlIHdlIHNlZSB0aGF0IHRoZSBiZXN0IG1ldGhvZCB0byBwcmVkaWN0IGZ1dHVyZSBkYW5jZWFiaWxpdHkgbWVkaWFuIHZhbHVlcyBmb3IgdGhlIG92ZXJhbGwgZGF0YSBzZXQgaXMgdGhyb3VnaCBOTi4gSG93ZXZlciwgZ2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDEwLjQlLCB0aGlzIGZlYXR1cmUgY2Fubm90IGJlIHJlbGlhYmx5IGZvcmVjYXN0IGludG8gdGhlIGZ1dHVyZS4KICAKCiMjIER1cmF0aW9uIG1vZGVscwoKYGBge3J9CmR1cmF0aW9uID0gdHMoYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssImR1cmF0aW9uX21zIl0pICwgc3RhcnQ9MTkyMSkKZHVyYXRpb24udHJhaW4gPSBzdWJzZXQoZHVyYXRpb24sIHN0YXJ0PTEsIGVuZD1uVHJhaW4pCmR1cmF0aW9uLnRlc3QgPSBzdWJzZXQoZHVyYXRpb24sIHN0YXJ0ID0gKG5UcmFpbisxKSwgZW5kID0oblRyYWluK25UZXN0KSkKCiNTRVMKZHVyYXRpb24udHJhaW4uc2VzIDwtIHNlcyhkdXJhdGlvbi50cmFpbiwgaD1uVGVzdCkKZHVyYXRpb24uc2VzLm1hcGUgPSBtYXBlKGR1cmF0aW9uLnRyYWluLnNlcyRtZWFuLCBkdXJhdGlvbi50ZXN0KQpwbG90KGR1cmF0aW9uLnRyYWluLnNlcywgbWFpbj1wYXN0ZSgiRHVyYXRpb24iLCAiU0VTIiksIHN1Yj1wYXN0ZSgiTUFQRToiLHJvdW5kKGR1cmF0aW9uLnNlcy5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhkdXJhdGlvbikKCiNTQVJJTUEKZHVyYXRpb24uc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEoZHVyYXRpb24udHJhaW4pCmR1cmF0aW9uLnNhcmltYSA8LSBmb3JlY2FzdChkdXJhdGlvbi5zYXJpbWEubW9kZWwsIGg9blRlc3QpCmR1cmF0aW9uLnNhcmltYS5tYXBlID0gbWFwZShkdXJhdGlvbi5zYXJpbWEkbWVhbiwgZHVyYXRpb24udGVzdCkKcGxvdChkdXJhdGlvbi5zYXJpbWEsIG1haW49cGFzdGUoIkR1cmF0aW9uIiwgIlNBUklNQSIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZHVyYXRpb24uc2FyaW1hLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGR1cmF0aW9uKQoKCiNOZXVyYWwgTmV0d29yawpkdXJhdGlvbi50cmFpbi5ubiA8LSBlbG0oZHVyYXRpb24udHJhaW4pCmR1cmF0aW9uLm5uLmZvcmVjYXN0IDwtIGZvcmVjYXN0KGR1cmF0aW9uLnRyYWluLm5uLCBoPW5UZXN0KQpkdXJhdGlvbi5ubi5tYXBlID0gbWFwZShkdXJhdGlvbi5ubi5mb3JlY2FzdCRtZWFuLCBkdXJhdGlvbi50ZXN0KQpwbG90KGR1cmF0aW9uLCBtYWluPXBhc3RlKCJEdXJhdGlvbiIsICJOTiIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoZHVyYXRpb24ubm4ubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModHMoZHVyYXRpb24ubm4uZm9yZWNhc3QkbWVhbiwgc3RhcnQ9MTkyMStuVHJhaW4pLCBjb2w9MikKYGBgCkhlcmUgd2Ugc2VlIHRoYXQgdGhlIGJlc3QgbWV0aG9kIHRvIHByZWRpY3QgZnV0dXJlIGR1cmF0aW9uIG1lZGlhbiB2YWx1ZXMgZm9yIHRoZSBvdmVyYWxsIGRhdGEgc2V0IGlzIHRocm91Z2ggTk4uIEdpdmVuIHRoYXQgdGhpcyBiZXN0IE1BUEUgaXMgcm91Z2hseSA4JSwgdGhpcyBmZWF0dXJlIGNhbiBiZSBmYWlybHkgcmVsaWFibHkgZm9yZWNhc3QgaW50byB0aGUgZnV0dXJlLgogIAoKIyMgVmFsZW5jZQpgYGB7cn0KdmFsZW5jZSA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJ2YWxlbmNlIl0pICwgc3RhcnQ9MTkyMSkKdmFsZW5jZS50cmFpbiA9IHN1YnNldCh2YWxlbmNlLCBzdGFydD0xLCBlbmQ9blRyYWluKQp2YWxlbmNlLnRlc3QgPSBzdWJzZXQodmFsZW5jZSwgc3RhcnQgPSAoblRyYWluKzEpLCBlbmQgPShuVHJhaW4rblRlc3QpKQoKI1NFUwp2YWxlbmNlLnRyYWluLnNlcyA8LSBzZXModmFsZW5jZS50cmFpbiwgaD1uVGVzdCkKdmFsZW5jZS5zZXMubWFwZSA9IG1hcGUodmFsZW5jZS50cmFpbi5zZXMkbWVhbiwgdmFsZW5jZS50ZXN0KQpwbG90KHZhbGVuY2UudHJhaW4uc2VzLCBtYWluPXBhc3RlKCJ2YWxlbmNlIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iixyb3VuZCh2YWxlbmNlLnNlcy5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh2YWxlbmNlKQoKI1NBUklNQQp2YWxlbmNlLnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKHZhbGVuY2UudHJhaW4pCnZhbGVuY2Uuc2FyaW1hIDwtIGZvcmVjYXN0KHZhbGVuY2Uuc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQp2YWxlbmNlLnNhcmltYS5tYXBlID0gbWFwZSh2YWxlbmNlLnNhcmltYSRtZWFuLCB2YWxlbmNlLnRlc3QpCnBsb3QodmFsZW5jZS5zYXJpbWEsIG1haW49cGFzdGUoInZhbGVuY2UiLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZCh2YWxlbmNlLnNhcmltYS5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh2YWxlbmNlKQoKCiNOZXVyYWwgTmV0d29yawp2YWxlbmNlLnRyYWluLm5uIDwtIGVsbSh2YWxlbmNlLnRyYWluKQp2YWxlbmNlLm5uLmZvcmVjYXN0IDwtIGZvcmVjYXN0KHZhbGVuY2UudHJhaW4ubm4sIGg9blRlc3QpCnZhbGVuY2Uubm4ubWFwZSA9IG1hcGUodmFsZW5jZS5ubi5mb3JlY2FzdCRtZWFuLCB2YWxlbmNlLnRlc3QpCnBsb3QodmFsZW5jZSwgbWFpbj1wYXN0ZSgidmFsZW5jZSIsICJOTiIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQodmFsZW5jZS5ubi5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyh2YWxlbmNlLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYAoKIyMgVGVtcG8gbW9kZWwKCmBgYHtyfQp0ZW1wbyA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJ0ZW1wbyJdKSAsIHN0YXJ0PTE5MjEpCnRlbXBvLnRyYWluID0gc3Vic2V0KHRlbXBvLCBzdGFydD0xLCBlbmQ9blRyYWluKQp0ZW1wby50ZXN0ID0gc3Vic2V0KHRlbXBvLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCnRlbXBvLnRyYWluLnNlcyA8LSBzZXModGVtcG8udHJhaW4sIGg9blRlc3QpCnRlbXBvLnNlcy5tYXBlID0gbWFwZSh0ZW1wby50cmFpbi5zZXMkbWVhbiwgdGVtcG8udGVzdCkKcGxvdCh0ZW1wby50cmFpbi5zZXMsIG1haW49cGFzdGUoInRlbXBvIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iixyb3VuZCh0ZW1wby5zZXMubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModGVtcG8pCgojU0FSSU1BCnRlbXBvLnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKHRlbXBvLnRyYWluKQp0ZW1wby5zYXJpbWEgPC0gZm9yZWNhc3QodGVtcG8uc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQp0ZW1wby5zYXJpbWEubWFwZSA9IG1hcGUodGVtcG8uc2FyaW1hJG1lYW4sIHRlbXBvLnRlc3QpCnBsb3QodGVtcG8uc2FyaW1hLCBtYWluPXBhc3RlKCJ0ZW1wbyIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKHRlbXBvLnNhcmltYS5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0ZW1wbykKCgojTmV1cmFsIE5ldHdvcmsKdGVtcG8udHJhaW4ubm4gPC0gZWxtKHRlbXBvLnRyYWluKQp0ZW1wby5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdCh0ZW1wby50cmFpbi5ubiwgaD1uVGVzdCkKdGVtcG8ubm4ubWFwZSA9IG1hcGUodGVtcG8ubm4uZm9yZWNhc3QkbWVhbiwgdGVtcG8udGVzdCkKcGxvdCh0ZW1wbywgbWFpbj1wYXN0ZSgidGVtcG8iLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKHRlbXBvLm5uLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKHRlbXBvLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYApIZXJlIHdlIHNlZSB0aGF0IHRoZSBiZXN0IG1ldGhvZCB0byBwcmVkaWN0IGZ1dHVyZSB0ZW1wbyBtZWRpYW4gdmFsdWVzIGZvciB0aGUgb3ZlcmFsbCBkYXRhIHNldCBpcyB0aHJvdWdoIFNFUy4gR2l2ZW4gdGhhdCB0aGlzIGJlc3QgTUFQRSBpcyByb3VnaGx5IDElLCB0aGlzIGZlYXR1cmUgY2FuIGJlIGZhaXJseSByZWxpYWJseSBmb3JlY2FzdCBpbnRvIHRoZSBmdXR1cmUuCiAgCgojIyBMaXZlbmVzcwpgYGB7cn0KbGl2ZW5lc3MgPSB0cyhhcy5udW1lcmljKGZlYXR1cmVfcXVhbnRpbGVzWywibGl2ZW5lc3MiXSkgLCBzdGFydD0xOTIxKQpsaXZlbmVzcy50cmFpbiA9IHN1YnNldChsaXZlbmVzcywgc3RhcnQ9MSwgZW5kPW5UcmFpbikKbGl2ZW5lc3MudGVzdCA9IHN1YnNldChsaXZlbmVzcywgc3RhcnQgPSAoblRyYWluKzEpLCBlbmQgPShuVHJhaW4rblRlc3QpKQoKI1NFUwpsaXZlbmVzcy50cmFpbi5zZXMgPC0gc2VzKGxpdmVuZXNzLnRyYWluLCBoPW5UZXN0KQpsaXZlbmVzcy5zZXMubWFwZSA9IG1hcGUobGl2ZW5lc3MudHJhaW4uc2VzJG1lYW4sIGxpdmVuZXNzLnRlc3QpCnBsb3QobGl2ZW5lc3MudHJhaW4uc2VzLCBtYWluPXBhc3RlKCJsaXZlbmVzcyIsICJTRVMiKSwgc3ViPXBhc3RlKCJNQVBFOiIscm91bmQobGl2ZW5lc3Muc2VzLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGxpdmVuZXNzKQoKI1NBUklNQQpsaXZlbmVzcy5zYXJpbWEubW9kZWwgPC0gYXV0by5hcmltYShsaXZlbmVzcy50cmFpbikKbGl2ZW5lc3Muc2FyaW1hIDwtIGZvcmVjYXN0KGxpdmVuZXNzLnNhcmltYS5tb2RlbCwgaD1uVGVzdCkKbGl2ZW5lc3Muc2FyaW1hLm1hcGUgPSBtYXBlKGxpdmVuZXNzLnNhcmltYSRtZWFuLCBsaXZlbmVzcy50ZXN0KQpwbG90KGxpdmVuZXNzLnNhcmltYSwgbWFpbj1wYXN0ZSgibGl2ZW5lc3MiLCAiU0FSSU1BIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChsaXZlbmVzcy5zYXJpbWEubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobGl2ZW5lc3MpCgoKI05ldXJhbCBOZXR3b3JrCmxpdmVuZXNzLnRyYWluLm5uIDwtIGVsbShsaXZlbmVzcy50cmFpbikKbGl2ZW5lc3Mubm4uZm9yZWNhc3QgPC0gZm9yZWNhc3QobGl2ZW5lc3MudHJhaW4ubm4sIGg9blRlc3QpCmxpdmVuZXNzLm5uLm1hcGUgPSBtYXBlKGxpdmVuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIGxpdmVuZXNzLnRlc3QpCnBsb3QobGl2ZW5lc3MsIG1haW49cGFzdGUoImxpdmVuZXNzIiwgIk5OIiksIHN1Yj1wYXN0ZSgiTUFQRToiLCByb3VuZChsaXZlbmVzcy5ubi5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh0cyhsaXZlbmVzcy5ubi5mb3JlY2FzdCRtZWFuLCBzdGFydD0xOTIxK25UcmFpbiksIGNvbD0yKQpgYGAKCiMjIExvdWRuZXNzCmBgYHtyfQpsb3VkbmVzcyA9IHRzKGFzLm51bWVyaWMoZmVhdHVyZV9xdWFudGlsZXNbLCJsb3VkbmVzcyJdKSAsIHN0YXJ0PTE5MjEpCmxvdWRuZXNzLnRyYWluID0gc3Vic2V0KGxvdWRuZXNzLCBzdGFydD0xLCBlbmQ9blRyYWluKQpsb3VkbmVzcy50ZXN0ID0gc3Vic2V0KGxvdWRuZXNzLCBzdGFydCA9IChuVHJhaW4rMSksIGVuZCA9KG5UcmFpbituVGVzdCkpCgojU0VTCmxvdWRuZXNzLnRyYWluLnNlcyA8LSBzZXMobG91ZG5lc3MudHJhaW4sIGg9blRlc3QpCmxvdWRuZXNzLnNlcy5tYXBlID0gbWFwZShsb3VkbmVzcy50cmFpbi5zZXMkbWVhbiwgbG91ZG5lc3MudGVzdCkKcGxvdChsb3VkbmVzcy50cmFpbi5zZXMsIG1haW49cGFzdGUoImxvdWRuZXNzIiwgIlNFUyIpLCBzdWI9cGFzdGUoIk1BUEU6Iixyb3VuZChsb3VkbmVzcy5zZXMubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobG91ZG5lc3MpCgojU0FSSU1BCmxvdWRuZXNzLnNhcmltYS5tb2RlbCA8LSBhdXRvLmFyaW1hKGxvdWRuZXNzLnRyYWluKQpsb3VkbmVzcy5zYXJpbWEgPC0gZm9yZWNhc3QobG91ZG5lc3Muc2FyaW1hLm1vZGVsLCBoPW5UZXN0KQpsb3VkbmVzcy5zYXJpbWEubWFwZSA9IG1hcGUobG91ZG5lc3Muc2FyaW1hJG1lYW4sIGxvdWRuZXNzLnRlc3QpCnBsb3QobG91ZG5lc3Muc2FyaW1hLCBtYWluPXBhc3RlKCJsb3VkbmVzcyIsICJTQVJJTUEiKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxvdWRuZXNzLnNhcmltYS5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhsb3VkbmVzcykKCgojTmV1cmFsIE5ldHdvcmsKbG91ZG5lc3MudHJhaW4ubm4gPC0gZWxtKGxvdWRuZXNzLnRyYWluKQpsb3VkbmVzcy5ubi5mb3JlY2FzdCA8LSBmb3JlY2FzdChsb3VkbmVzcy50cmFpbi5ubiwgaD1uVGVzdCkKbG91ZG5lc3Mubm4ubWFwZSA9IG1hcGUobG91ZG5lc3Mubm4uZm9yZWNhc3QkbWVhbiwgbG91ZG5lc3MudGVzdCkKcGxvdChsb3VkbmVzcywgbWFpbj1wYXN0ZSgibG91ZG5lc3MiLCAiTk4iKSwgc3ViPXBhc3RlKCJNQVBFOiIsIHJvdW5kKGxvdWRuZXNzLm5uLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHRzKGxvdWRuZXNzLm5uLmZvcmVjYXN0JG1lYW4sIHN0YXJ0PTE5MjErblRyYWluKSwgY29sPTIpCmBgYAoKIyMgU3BlZWNoaW5lc3MKYGBge3J9CnNwZWVjaGluZXNzID0gdHMoYXMubnVtZXJpYyhmZWF0dXJlX3F1YW50aWxlc1ssInNwZWVjaGluZXNzIl0pICwgc3RhcnQ9MTkyMSkKc3BlZWNoaW5lc3MudHJhaW4gPSBzdWJzZXQoc3BlZWNoaW5lc3MsIHN0YXJ0PTEsIGVuZD1uVHJhaW4pCnNwZWVjaGluZXNzLnRlc3QgPSBzdWJzZXQoc3BlZWNoaW5lc3MsIHN0YXJ0ID0gKG5UcmFpbisxKSwgZW5kID0oblRyYWluK25UZXN0KSkKCiNTRVMKc3BlZWNoaW5lc3MudHJhaW4uc2VzIDwtIHNlcyhzcGVlY2hpbmVzcy50cmFpbiwgaD1uVGVzdCkKc3BlZWNoaW5lc3Muc2VzLm1hcGUgPSBtYXBlKHNwZWVjaGluZXNzLnRyYWluLnNlcyRtZWFuLCBzcGVlY2hpbmVzcy50ZXN0KQpwbG90KHNwZWVjaGluZXNzLnRyYWluLnNlcywgbWFpbj1wYXN0ZSgic3BlZWNoaW5lc3MiLCAiU0VTIiksIHN1Yj1wYXN0ZSgiTUFQRToiLHJvdW5kKHNwZWVjaGluZXNzLnNlcy5tYXBlKjEwMCwgMyksICIlIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhzcGVlY2hpbmVzcykKCiNTQVJJTUEKc3BlZWNoaW5lc3Muc2FyaW1hLm1vZGVsIDwtIGF1dG8uYXJpbWEoc3BlZWNoaW5lc3MudHJhaW4pCnNwZWVjaGluZXNzLnNhcmltYSA8LSBmb3JlY2FzdChzcGVlY2hpbmVzcy5zYXJpbWEubW9kZWwsIGg9blRlc3QpCnNwZWVjaGluZXNzLnNhcmltYS5tYXBlID0gbWFwZShzcGVlY2hpbmVzcy5zYXJpbWEkbWVhbiwgc3BlZWNoaW5lc3MudGVzdCkKcGxvdChzcGVlY2hpbmVzcy5zYXJpbWEsIG1haW49cGFzdGUoInNwZWVjaGluZXNzIiwgIlNBUklNQSIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoc3BlZWNoaW5lc3Muc2FyaW1hLm1hcGUqMTAwLCAzKSwgIiUiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKHNwZWVjaGluZXNzKQoKCiNOZXVyYWwgTmV0d29yawpzcGVlY2hpbmVzcy50cmFpbi5ubiA8LSBlbG0oc3BlZWNoaW5lc3MudHJhaW4pCnNwZWVjaGluZXNzLm5uLmZvcmVjYXN0IDwtIGZvcmVjYXN0KHNwZWVjaGluZXNzLnRyYWluLm5uLCBoPW5UZXN0KQpzcGVlY2hpbmVzcy5ubi5tYXBlID0gbWFwZShzcGVlY2hpbmVzcy5ubi5mb3JlY2FzdCRtZWFuLCBzcGVlY2hpbmVzcy50ZXN0KQpwbG90KHNwZWVjaGluZXNzLCBtYWluPXBhc3RlKCJzcGVlY2hpbmVzcyIsICJOTiIpLCBzdWI9cGFzdGUoIk1BUEU6Iiwgcm91bmQoc3BlZWNoaW5lc3Mubm4ubWFwZSoxMDAsIDMpLCAiJSIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModHMoc3BlZWNoaW5lc3Mubm4uZm9yZWNhc3QkbWVhbiwgc3RhcnQ9MTkyMStuVHJhaW4pLCBjb2w9MikKYGBgCgojIExpbmVhciBNb2RlbCBmb3IgT3ZlcmFsbCBGZWF0dXJlIE1lZGlhbi0tPk1vc3QgUG9wdWxhciBTb25ncycgRmVhdHVyZSBNZWRpYW4KIyMjIFdlIHdpbGwgb25seSBjb25zaWRlciBtb2RlbHMgdGhhdCBoYWQgYSBsb3cgZW5vdWdoIE1BUEVzICgxMCUgY2hvc2VuIGFzIGEgdGhyZXNob2xkKSBpbiB0aGVpciBiZXN0IHRpbWVzZXJpZXMgZm9yZWNhc3RpbmcgbW9kZWwuCiMjIyBIZXJlIE1BUEUgaXMgdGhlIGRpZmZlcmVuY2UgYmV0d2VlbiB0aGUgYWN0dWFsIG1lZGlhbiBmb3IgbW9zdCBwb3B1bGFyIHNvbmdzIGFuZCB0aGUgcHJlZGljdGVkIG1lZGlhbi4KCmBgYHtyfQojRW5lcmd5CmJlc3RfZW5lcmd5ID0gdHMoYXMubnVtZXJpYyhiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzWywiZW5lcmd5Il0pLCBzdGFydD0xOTIxLCBlbmQ9MjAyMCkgCmJlc3RfZW5lcmd5LnRyYWluID0gc3Vic2V0KGJlc3RfZW5lcmd5LCBzdGFydCA9IDEsIGVuZCA9IG5UcmFpbikKYmVzdF9lbmVyZ3kudGVzdCA9IHN1YnNldChiZXN0X2VuZXJneSwgc3RhcnQgPSBuVHJhaW4rMSwgZW5kID0gblRyYWluK25UZXN0KQp0cmFpbl9kZiA9IGRhdGEuZnJhbWUoYmVzdCA9IGJlc3RfZW5lcmd5LnRyYWluLCBvdmVyYWxsID0gZW5lcmd5LnRyYWluKQoKYmVzdCA9IGJlc3RfZW5lcmd5LnRyYWluCm92ZXJhbGwgPSBlbmVyZ3kudHJhaW4KCmVuZXJneS5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKdGVzdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSBlbmVyZ3kudGVzdCkKYmVzdF9lbmVyZ3kudGVzdC5wcmVkaWN0IDwtIHByZWRpY3QoZW5lcmd5LmxtLCBuZXdkYXRhPXRlc3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKYmVzdF9lbmVyZ3lfdGVzdF9wcmVkaWN0Lm1hcGUgPC0gbWFwZShiZXN0X2VuZXJneS50ZXN0WzE6bGVuZ3RoKGJlc3RfZW5lcmd5LnRlc3QpXSwgYmVzdF9lbmVyZ3kudGVzdC5wcmVkaWN0WywxXSkKcGxvdChiZXN0X2VuZXJneSx4bGFiPSJZZWFyIiwgeWxhYj0iRW5lcmd5IiwgbWFpbj1wYXN0ZSgiRW5lcmd5IFBvcHVsYXJpdHkgUHJlZGljdGlvbiIpLCBzdWIgPSBwYXN0ZSgiTUFQRTogIiwgcm91bmQoYmVzdF9lbmVyZ3lfdGVzdF9wcmVkaWN0Lm1hcGUqMTAwLCAzKSwgIiUiKSkKbGluZXModHMoYmVzdF9lbmVyZ3kudGVzdC5wcmVkaWN0WywxXSwgc3RhcnQgPSAxOTIxK25UcmFpbisxLCBlbmQgPSAyMDIwKSwgY29sID0gMikKCiNEdXJhdGlvbgpiZXN0X2R1cmF0aW9uID0gdHMoYXMubnVtZXJpYyhiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzWywiZHVyYXRpb25fbXMiXSksIHN0YXJ0PTE5MjEsIGVuZD0yMDIwKSAKYmVzdF9kdXJhdGlvbi50cmFpbiA9IHN1YnNldChiZXN0X2R1cmF0aW9uLCBzdGFydCA9IDEsIGVuZCA9IG5UcmFpbikKYmVzdF9kdXJhdGlvbi50ZXN0ID0gc3Vic2V0KGJlc3RfZHVyYXRpb24sIHN0YXJ0ID0gblRyYWluKzEsIGVuZCA9IG5UcmFpbituVGVzdCkKdHJhaW5fZGYgPSBkYXRhLmZyYW1lKGJlc3QgPSBiZXN0X2R1cmF0aW9uLnRyYWluLCBvdmVyYWxsID0gZHVyYXRpb24udHJhaW4pCgpiZXN0ID0gYmVzdF9kdXJhdGlvbi50cmFpbgpvdmVyYWxsID0gZHVyYXRpb24udHJhaW4KCmR1cmF0aW9uLmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQp0ZXN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGR1cmF0aW9uLnRlc3QpCmJlc3RfZHVyYXRpb24udGVzdC5wcmVkaWN0IDwtIHByZWRpY3QoZHVyYXRpb24ubG0sIG5ld2RhdGE9dGVzdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpiZXN0X2R1cmF0aW9uX3Rlc3QucHJlZGljdC5tYXBlIDwtIG1hcGUoYmVzdF9kdXJhdGlvbi50ZXN0WzE6bGVuZ3RoKGJlc3RfZHVyYXRpb24udGVzdCldLCBiZXN0X2R1cmF0aW9uLnRlc3QucHJlZGljdFssMV0pCnBsb3QoYmVzdF9kdXJhdGlvbix4bGFiPSJZZWFyIiwgeWxhYj0iZHVyYXRpb24iLCBtYWluPXBhc3RlKCJkdXJhdGlvbiBQb3B1bGFyaXR5IFByZWRpY3Rpb24iKSwgc3ViID0gcGFzdGUoIk1BUEU6ICIsIHJvdW5kKGJlc3RfZHVyYXRpb25fdGVzdC5wcmVkaWN0Lm1hcGUqMTAwLCAzKSwgIiUiKSkKbGluZXModHMoYmVzdF9kdXJhdGlvbi50ZXN0LnByZWRpY3RbLDFdLCBzdGFydCA9IDE5MjErblRyYWluKzEsIGVuZCA9IDIwMjApLCBjb2wgPSAyKQoKI0FjY291c3RpY25lc3MKYmVzdF9hY291c3RpY25lc3MgPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJhY291c3RpY25lc3MiXSksIHN0YXJ0PTE5MjEsIGVuZD0yMDIwKSAKYmVzdF9hY291c3RpY25lc3MudHJhaW4gPSBzdWJzZXQoYmVzdF9hY291c3RpY25lc3MsIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X2Fjb3VzdGljbmVzcy50ZXN0ID0gc3Vic2V0KGJlc3RfYWNvdXN0aWNuZXNzLCBzdGFydCA9IG5UcmFpbisxLCBlbmQgPSBuVHJhaW4rblRlc3QpCnRyYWluX2RmID0gZGF0YS5mcmFtZShiZXN0ID0gYmVzdF9hY291c3RpY25lc3MudHJhaW4sIG92ZXJhbGwgPSBhY291c3RpY25lc3MudHJhaW4pCgpiZXN0ID0gYmVzdF9hY291c3RpY25lc3MudHJhaW4Kb3ZlcmFsbCA9IGFjb3VzdGljbmVzcy50cmFpbgoKYWNvdXN0aWNuZXNzLmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQp0ZXN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGFjb3VzdGljbmVzcy50ZXN0KQpiZXN0X2Fjb3VzdGljbmVzcy50ZXN0LnByZWRpY3QgPC0gcHJlZGljdChhY291c3RpY25lc3MubG0sIG5ld2RhdGE9dGVzdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpiZXN0X2Fjb3VzdGljbmVzc190ZXN0LnByZWRpY3QubWFwZSA8LSBtYXBlKGJlc3RfYWNvdXN0aWNuZXNzLnRlc3RbMTpsZW5ndGgoYmVzdF9hY291c3RpY25lc3MudGVzdCldLCBiZXN0X2Fjb3VzdGljbmVzcy50ZXN0LnByZWRpY3RbLDFdKQpwbG90KGJlc3RfYWNvdXN0aWNuZXNzLHhsYWI9IlllYXIiLCB5bGFiPSJhY291c3RpY25lc3MiLCBtYWluPXBhc3RlKCJhY291c3RpY25lc3MgUG9wdWxhcml0eSBQcmVkaWN0aW9uIiksIHN1YiA9IHBhc3RlKCJNQVBFOiAiLCByb3VuZChiZXN0X2Fjb3VzdGljbmVzc190ZXN0LnByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X2Fjb3VzdGljbmVzcy50ZXN0LnByZWRpY3RbLDFdLCBzdGFydCA9IDE5MjErblRyYWluKzEsIGVuZCA9IDIwMjApLCBjb2wgPSAyKQoKI1ZhbGVuY2UKYmVzdF92YWxlbmNlID0gdHMoYXMubnVtZXJpYyhiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzWywidmFsZW5jZSJdKSwgc3RhcnQ9MTkyMSwgZW5kPTIwMjApIApiZXN0X3ZhbGVuY2UudHJhaW4gPSBzdWJzZXQoYmVzdF92YWxlbmNlLCBzdGFydCA9IDEsIGVuZCA9IG5UcmFpbikKYmVzdF92YWxlbmNlLnRlc3QgPSBzdWJzZXQoYmVzdF92YWxlbmNlLCBzdGFydCA9IG5UcmFpbisxLCBlbmQgPSBuVHJhaW4rblRlc3QpCnRyYWluX2RmID0gZGF0YS5mcmFtZShiZXN0ID0gYmVzdF92YWxlbmNlLnRyYWluLCBvdmVyYWxsID0gdmFsZW5jZS50cmFpbikKCmJlc3QgPSBiZXN0X3ZhbGVuY2UudHJhaW4Kb3ZlcmFsbCA9IHZhbGVuY2UudHJhaW4KCnZhbGVuY2UubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnRlc3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gdmFsZW5jZS50ZXN0KQpiZXN0X3ZhbGVuY2UudGVzdC5wcmVkaWN0IDwtIHByZWRpY3QodmFsZW5jZS5sbSwgbmV3ZGF0YT10ZXN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCmJlc3RfdmFsZW5jZV90ZXN0LnByZWRpY3QubWFwZSA8LSBtYXBlKGJlc3RfdmFsZW5jZS50ZXN0WzE6bGVuZ3RoKGJlc3RfdmFsZW5jZS50ZXN0KV0sIGJlc3RfdmFsZW5jZS50ZXN0LnByZWRpY3RbLDFdKQpwbG90KGJlc3RfdmFsZW5jZSx4bGFiPSJZZWFyIiwgeWxhYj0iVmFsZW5jZSIsIG1haW49cGFzdGUoIlZhbGVuY2UgUG9wdWxhcml0eSBQcmVkaWN0aW9uIiksIHN1YiA9IHBhc3RlKCJNQVBFOiAiLCByb3VuZChiZXN0X3ZhbGVuY2VfdGVzdC5wcmVkaWN0Lm1hcGUqMTAwLCAzKSwgIiUiKSkKbGluZXModHMoYmVzdF92YWxlbmNlLnRlc3QucHJlZGljdFssMV0sIHN0YXJ0ID0gMTkyMStuVHJhaW4rMSwgZW5kID0gMjAyMCksIGNvbCA9IDIpCgojVGVtcG8KYmVzdF90ZW1wbyA9IHRzKGFzLm51bWVyaWMoYmVzdF9mZWF0dXJlX3F1YW50aWxlc1ssInRlbXBvIl0pLCBzdGFydD0xOTIxLCBlbmQ9MjAyMCkgCmJlc3RfdGVtcG8udHJhaW4gPSBzdWJzZXQoYmVzdF90ZW1wbywgc3RhcnQgPSAxLCBlbmQgPSBuVHJhaW4pCmJlc3RfdGVtcG8udGVzdCA9IHN1YnNldChiZXN0X3RlbXBvLCBzdGFydCA9IG5UcmFpbisxLCBlbmQgPSBuVHJhaW4rblRlc3QpCnRyYWluX2RmID0gZGF0YS5mcmFtZShiZXN0ID0gYmVzdF90ZW1wby50cmFpbiwgb3ZlcmFsbCA9IHRlbXBvLnRyYWluKQoKYmVzdCA9IGJlc3RfdGVtcG8udHJhaW4Kb3ZlcmFsbCA9IHRlbXBvLnRyYWluCgp0ZW1wby5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKdGVzdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSB0ZW1wby50ZXN0KQpiZXN0X3RlbXBvLnRlc3QucHJlZGljdCA8LSBwcmVkaWN0KHRlbXBvLmxtLCBuZXdkYXRhPXRlc3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKYmVzdF90ZW1wb190ZXN0LnByZWRpY3QubWFwZSA8LSBtYXBlKGJlc3RfdGVtcG8udGVzdFsxOmxlbmd0aChiZXN0X3RlbXBvLnRlc3QpXSwgYmVzdF90ZW1wby50ZXN0LnByZWRpY3RbLDFdKQpwbG90KGJlc3RfdGVtcG8seGxhYj0iWWVhciIsIHlsYWI9InRlbXBvIiwgbWFpbj1wYXN0ZSgidGVtcG8gUG9wdWxhcml0eSBQcmVkaWN0aW9uIiksIHN1YiA9IHBhc3RlKCJNQVBFOiAiLCByb3VuZChiZXN0X3RlbXBvX3Rlc3QucHJlZGljdC5tYXBlKjEwMCwgMyksICIlIikpCmxpbmVzKHRzKGJlc3RfdGVtcG8udGVzdC5wcmVkaWN0WywxXSwgc3RhcnQgPSAxOTIxK25UcmFpbisxLCBlbmQgPSAyMDIwKSwgY29sID0gMikKCiNMaXZlbmVzcwpiZXN0X2xpdmVuZXNzID0gdHMoYXMubnVtZXJpYyhiZXN0X2ZlYXR1cmVfcXVhbnRpbGVzWywibGl2ZW5lc3MiXSksIHN0YXJ0PTE5MjEsIGVuZD0yMDIwKSAKYmVzdF9saXZlbmVzcy50cmFpbiA9IHN1YnNldChiZXN0X2xpdmVuZXNzLCBzdGFydCA9IDEsIGVuZCA9IG5UcmFpbikKYmVzdF9saXZlbmVzcy50ZXN0ID0gc3Vic2V0KGJlc3RfbGl2ZW5lc3MsIHN0YXJ0ID0gblRyYWluKzEsIGVuZCA9IG5UcmFpbituVGVzdCkKdHJhaW5fZGYgPSBkYXRhLmZyYW1lKGJlc3QgPSBiZXN0X2xpdmVuZXNzLnRyYWluLCBvdmVyYWxsID0gbGl2ZW5lc3MudHJhaW4pCgpiZXN0ID0gYmVzdF9saXZlbmVzcy50cmFpbgpvdmVyYWxsID0gbGl2ZW5lc3MudHJhaW4KCmxpdmVuZXNzLmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQp0ZXN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGxpdmVuZXNzLnRlc3QpCmJlc3RfbGl2ZW5lc3MudGVzdC5wcmVkaWN0IDwtIHByZWRpY3QobGl2ZW5lc3MubG0sIG5ld2RhdGE9dGVzdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQpiZXN0X2xpdmVuZXNzX3Rlc3QucHJlZGljdC5tYXBlIDwtIG1hcGUoYmVzdF9saXZlbmVzcy50ZXN0WzE6bGVuZ3RoKGJlc3RfbGl2ZW5lc3MudGVzdCldLCBiZXN0X2xpdmVuZXNzLnRlc3QucHJlZGljdFssMV0pCnBsb3QoYmVzdF9saXZlbmVzcyx4bGFiPSJZZWFyIiwgeWxhYj0ibGl2ZW5lc3MiLCBtYWluPXBhc3RlKCJsaXZlbmVzcyBQb3B1bGFyaXR5IFByZWRpY3Rpb24iKSwgc3ViID0gcGFzdGUoIk1BUEU6ICIsIHJvdW5kKGJlc3RfbGl2ZW5lc3NfdGVzdC5wcmVkaWN0Lm1hcGUqMTAwLCAzKSwgIiUiKSkKbGluZXModHMoYmVzdF9saXZlbmVzcy50ZXN0LnByZWRpY3RbLDFdLCBzdGFydCA9IDE5MjErblRyYWluKzEsIGVuZCA9IDIwMjApLCBjb2wgPSAyKQoKI0xvdWRuZXNzCmJlc3RfbG91ZG5lc3MgPSB0cyhhcy5udW1lcmljKGJlc3RfZmVhdHVyZV9xdWFudGlsZXNbLCJsb3VkbmVzcyJdKSwgc3RhcnQ9MTkyMSwgZW5kPTIwMjApIApiZXN0X2xvdWRuZXNzLnRyYWluID0gc3Vic2V0KGJlc3RfbG91ZG5lc3MsIHN0YXJ0ID0gMSwgZW5kID0gblRyYWluKQpiZXN0X2xvdWRuZXNzLnRlc3QgPSBzdWJzZXQoYmVzdF9sb3VkbmVzcywgc3RhcnQgPSBuVHJhaW4rMSwgZW5kID0gblRyYWluK25UZXN0KQp0cmFpbl9kZiA9IGRhdGEuZnJhbWUoYmVzdCA9IGJlc3RfbG91ZG5lc3MudHJhaW4sIG92ZXJhbGwgPSBsb3VkbmVzcy50cmFpbikKCmJlc3QgPSBiZXN0X2xvdWRuZXNzLnRyYWluCm92ZXJhbGwgPSBsb3VkbmVzcy50cmFpbgoKbG91ZG5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnRlc3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbG91ZG5lc3MudGVzdCkKYmVzdF9sb3VkbmVzcy50ZXN0LnByZWRpY3QgPC0gcHJlZGljdChsb3VkbmVzcy5sbSwgbmV3ZGF0YT10ZXN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCmJlc3RfbG91ZG5lc3NfdGVzdC5wcmVkaWN0Lm1hcGUgPC0gbWFwZShiZXN0X2xvdWRuZXNzLnRlc3RbMTpsZW5ndGgoYmVzdF9sb3VkbmVzcy50ZXN0KV0sIGJlc3RfbG91ZG5lc3MudGVzdC5wcmVkaWN0WywxXSkKcGxvdChiZXN0X2xvdWRuZXNzLHhsYWI9IlllYXIiLCB5bGFiPSJsb3VkbmVzcyIsIG1haW49cGFzdGUoImxvdWRuZXNzIFBvcHVsYXJpdHkgUHJlZGljdGlvbiIpLCBzdWIgPSBwYXN0ZSgiTUFQRTogIiwgcm91bmQoYmVzdF9sb3VkbmVzc190ZXN0LnByZWRpY3QubWFwZSoxMDAsIDMpLCAiJSIpKQpsaW5lcyh0cyhiZXN0X2xvdWRuZXNzLnRlc3QucHJlZGljdFssMV0sIHN0YXJ0ID0gMTkyMStuVHJhaW4rMSwgZW5kID0gMjAyMCksIGNvbCA9IDIpCmBgYAoKIyBQcmVkaWN0IGZlYXR1cmVzIGZvciB0aGUgb3ZlcmFsbCBtYXJrZXQgdHJlbmRzIGZvciB0aGUgbmV4dCBmaXZlIHllYXJzLgojIyMgT25seSBpbmNsdWRpbmcgZmVhdHVyZXMgd2hvc2UgdHJhaW5lZCBsaW5lYXIgbW9kZWxzIGhhZCBhIE1BUEUgYmVsb3cgMTAlLCBhcyB0aGVzZSBhcmUgdGhlIGZlYXR1cmVzIHdob3NlIG1vc3QgcG9wdWxhciBtZWRpYW5zIHdpbGwgYmUgbW9zdCByZWxpYWJseSBwcmVkaWN0ZWQgYnkgdGhlaXIgZm9yZWNhc3Qgb3ZlcmFsbCB2YWx1ZXMuCiMjIyBVc2luZyB0aGUgYmVzdCB0aW1lc2VyaWVzIG1ldGhvZCBzZWVuIGZvciBlYWNoIGZlYXR1cmUuCgojIyBFbmVyZ3k6IFNFUyBNb2RlbAoKYGBge3J9CmVuZXJneS5zZXMgPC0gc2VzKGVuZXJneSwgaD1uVGVzdCkKcGxvdChlbmVyZ3kuc2VzLCBtYWluPXBhc3RlKCJFbmVyZ3kiLCAiU0VTIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyhlbmVyZ3kpCmBgYAoKIyMgRHVyYXRpb246IE5OIE1vZGVsCgpgYGB7cn0KZHVyYXRpb24ubm4gPC0gZWxtKGR1cmF0aW9uKQpkdXJhdGlvbi5ubi5wcmVkaWN0IDwtIGZvcmVjYXN0KGR1cmF0aW9uLm5uLCBoPW5UZXN0KQpwbG90KGR1cmF0aW9uLm5uLnByZWRpY3QsIG1haW49cGFzdGUoIkR1cmF0aW9uIiwgIk5OIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpgYGAKCiMjIFRlbXBvOiBTRVMgTW9kZWwgCgpgYGB7cn0KdGVtcG8uc2VzIDwtIHNlcyh0ZW1wbywgaD1uVGVzdCkKcGxvdCh0ZW1wby5zZXMsIG1haW49cGFzdGUoIlRlbXBvIiwgIlNFUyIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXModGVtcG8pCmBgYAoKIyMgVmFsZW5jZTogU0FSSU1BIE1vZGVsCgpgYGB7cn0KdmFsZW5jZS5zYXJpbWEgPC0gYXV0by5hcmltYSh2YWxlbmNlKQp2YWxlbmNlLnNhcmltYS5wcmVkaWN0IDwtIGZvcmVjYXN0KHZhbGVuY2Uuc2FyaW1hLCBoPW5UZXN0KQpwbG90KHZhbGVuY2Uuc2FyaW1hLnByZWRpY3QsIG1haW49cGFzdGUoIlZhbGVuY2UiLCAiU0FSSU1BIiksIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiKQpsaW5lcyh2YWxlbmNlKQpgYGAKCiMjIExpdmVuZXNzOiBTQVJJTUEgTW9kZWwKCmBgYHtyfQpsaXZlbmVzcy5zYXJpbWEgPC0gYXV0by5hcmltYShsaXZlbmVzcykKbGl2ZW5lc3Muc2FyaW1hLnByZWRpY3QgPC0gZm9yZWNhc3QobGl2ZW5lc3Muc2FyaW1hLCBoPW5UZXN0KQpwbG90KGxpdmVuZXNzLnNhcmltYS5wcmVkaWN0LCBtYWluPXBhc3RlKCJsaXZlbmVzcyIsICJTQVJJTUEiKSwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIpCmxpbmVzKGxpdmVuZXNzKQpgYGAKCiMjIExvdWRuZXNzOiBTRVMgTW9kZWwgCgpgYGB7cn0KbG91ZG5lc3Muc2VzIDwtIHNlcyhsb3VkbmVzcywgaD1uVGVzdCkKcGxvdChsb3VkbmVzcy5zZXMsIG1haW49cGFzdGUoImxvdWRuZXNzIiwgIlNFUyIpLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIikKbGluZXMobG91ZG5lc3MpCmBgYAoKCgoKCiMgTm93IHByZWRpY3RpbmcgdGhlIG1lZGlhbiB2YWx1ZXMgZm9yIHRoZSAqbW9zdCBwb3B1bGFyIHNvbmdzKiBmb3IgdGhpcyBzZXQgb2YgZmVhdHVyZXMuCiMjIyBUaGUgZm9yZWNhc3Qgb3ZlcmFsbCBtZWRpYW4gdmFsdWVzIGFuZCBsaW5lYXIgbW9kZWwgcHJlZGljdGluZyB0aGUgbW9zdCBwb3B1bGFyIG1lZGlhbiB2YWx1ZXMgZm9yIGVhY2ggZmVhdHVyZSBnaXZlbiBvdmVyYWxsIG1lZGlhbiB2YWx1ZSB3aWxsIGJlIHVzZWQgdG8gcHJlZGljdCB0aGUgZnV0dXJlIG1lZGlhbiB2YWx1ZXMgZm9yIHBvcHVsYXIgc29uZ3MgZm9yIGVhY2ggZmVhdHVyZS4KCiMjIEVuZXJneQoKYGBge3J9CmJlc3QgPSBiZXN0X2VuZXJneQpvdmVyYWxsID0gZW5lcmd5CgoKZW5lcmd5LmxtIDwtIGxtKGJlc3QgfiBvdmVyYWxsKQpwcmVkaWN0X2RmIDwtIGRhdGEuZnJhbWUob3ZlcmFsbCA9IGVuZXJneS5zZXMkbWVhbikKZW5lcmd5LnByZWRpY3QgPC0gcHJlZGljdChlbmVyZ3kubG0sIG5ld2RhdGE9cHJlZGljdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQoKc21hbGxlc3QgPSBtaW4obWluKGJlc3QpLCBtaW4ob3ZlcmFsbCkpCmJpZ2dlc3QgPSBtYXgobWF4KGJlc3QpLCBtYXgob3ZlcmFsbCkpCnBsb3QoZW5lcmd5LCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIiwgeWxpbT1jKHNtYWxsZXN0LCBiaWdnZXN0KSwgbWFpbj0iTW9zdCBQb3B1bGFyIFNvbmcncyBFbmVyZ3kgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChlbmVyZ3kuc2VzJG1lYW4sIDMpLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfZW5lcmd5LCBjb2wgPSAyKQpsaW5lcyh0cyhlbmVyZ3kucHJlZGljdFssMV0sIHN0YXJ0PTIwMjArMSwgZW5kPTIwMjArblRlc3QpLCBjb2wgPSAzKQpsZWdlbmQoImJvdHRvbXJpZ2h0IiwgbHR5PTEsIGNvbD1jKDEsMiwzKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIiwgIk1vc3QgUG9wdWxhciBQcm9qZWN0aW9uIikpCmBgYAoKCiMjIER1cmF0aW9uCgpgYGB7cn0KYmVzdCA9IGJlc3RfZHVyYXRpb24Kb3ZlcmFsbCA9IGR1cmF0aW9uCgpkdXJhdGlvbi5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKcHJlZGljdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSBkdXJhdGlvbi5ubi5wcmVkaWN0JG1lYW4pCmR1cmF0aW9uLnByZWRpY3QgPC0gcHJlZGljdChkdXJhdGlvbi5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgpzbWFsbGVzdCA9IG1pbihtaW4oYmVzdCksIG1pbihvdmVyYWxsKSkKYmlnZ2VzdCA9IG1heChtYXgoYmVzdCksIG1heChvdmVyYWxsKSkKcGxvdChkdXJhdGlvbiwgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49Ik1vc3QgUG9wdWxhciBTb25nJ3MgRHVyYXRpb24gRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChkdXJhdGlvbi5ubi5wcmVkaWN0JG1lYW4pLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfZHVyYXRpb24sIGNvbCA9IDIpCmxpbmVzKHRzKGR1cmF0aW9uLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJ0b3ByaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBUZW1wbwoKYGBge3J9CmJlc3QgPSBiZXN0X3RlbXBvCm92ZXJhbGwgPSB0ZW1wbwoKdGVtcG8ubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gdGVtcG8uc2VzJG1lYW4pCnRlbXBvLnByZWRpY3QgPC0gcHJlZGljdCh0ZW1wby5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgoKc21hbGxlc3QgPSBtaW4obWluKGJlc3QpLCBtaW4ob3ZlcmFsbCkpCmJpZ2dlc3QgPSBtYXgobWF4KGJlc3QpLCBtYXgob3ZlcmFsbCkpCnBsb3QodGVtcG8sIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiLCB5bGltPWMoc21hbGxlc3QsIGJpZ2dlc3QpLCBtYWluPSJNb3N0IFBvcHVsYXIgU29uZydzIFRlbXBvIEZlYXR1cmUgUHJlZGljdGlvbiIsIHN1Yj1wYXN0ZSgiUHJvamVjdGVkIG1lZGlhbiBmZWF0dXJlIHZhbHVlIGZvciBtb3N0IHBvcHVsYXIgc29uZ3M6IiwgcGFzdGUocm91bmQodGVtcG8uc2VzJG1lYW4pLCBjb2xsYXBzZT0iLCIpICkpCmxpbmVzKGJlc3RfdGVtcG8sIGNvbCA9IDIpCmxpbmVzKHRzKHRlbXBvLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJib3R0b21yaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBWYWxlbmNlCgpgYGB7cn0KYmVzdCA9IGJlc3RfdmFsZW5jZQpvdmVyYWxsID0gdmFsZW5jZQoKdmFsZW5jZS5sbSA8LSBsbShiZXN0IH4gb3ZlcmFsbCkKcHJlZGljdF9kZiA8LSBkYXRhLmZyYW1lKG92ZXJhbGwgPSB2YWxlbmNlLnNhcmltYS5wcmVkaWN0JG1lYW4pCnZhbGVuY2UucHJlZGljdCA8LSBwcmVkaWN0KHZhbGVuY2UubG0sIG5ld2RhdGE9cHJlZGljdF9kZiwgaW50ZXJ2YWw9InByZWRpY3Rpb24iKQoKCnNtYWxsZXN0ID0gbWluKG1pbihiZXN0KSwgbWluKG92ZXJhbGwpKQpiaWdnZXN0ID0gbWF4KG1heChiZXN0KSwgbWF4KG92ZXJhbGwpKQpwbG90KHZhbGVuY2UsIHhsYWI9IlllYXIiLCB5bGFiPSJNZWRpYW4gVmFsdWUiLCB5bGltPWMoc21hbGxlc3QsIGJpZ2dlc3QpLCBtYWluPSJNb3N0IFBvcHVsYXIgU29uZydzIHZhbGVuY2UgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZCh2YWxlbmNlLnNhcmltYS5wcmVkaWN0JG1lYW4sMyksIGNvbGxhcHNlPSIsIikgKSkKbGluZXMoYmVzdF92YWxlbmNlLCBjb2wgPSAyKQpsaW5lcyh0cyh2YWxlbmNlLnByZWRpY3RbLDFdLCBzdGFydD0yMDIwKzEsIGVuZD0yMDIwK25UZXN0KSwgY29sID0gMykKbGVnZW5kKCJib3R0b21yaWdodCIsIGx0eT0xLCBjb2w9YygxLDIsMyksIGxlZ2VuZD1jKCJPdmVyYWxsIiwgIk1vc3QgUG9wdWxhciIsICJNb3N0IFBvcHVsYXIgUHJvamVjdGlvbiIpKQpgYGAKCgojIyBMaXZlbmVzcwoKYGBge3J9CmJlc3QgPSBiZXN0X2xpdmVuZXNzCm92ZXJhbGwgPSBsaXZlbmVzcwoKbGl2ZW5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbGl2ZW5lc3Muc2FyaW1hLnByZWRpY3QkbWVhbikKbGl2ZW5lc3MucHJlZGljdCA8LSBwcmVkaWN0KGxpdmVuZXNzLmxtLCBuZXdkYXRhPXByZWRpY3RfZGYsIGludGVydmFsPSJwcmVkaWN0aW9uIikKCnNtYWxsZXN0ID0gbWluKG1pbihiZXN0KSwgbWluKG92ZXJhbGwpKQpiaWdnZXN0ID0gbWF4KG1heChiZXN0KSwgbWF4KG92ZXJhbGwpKQpwbG90KGxpdmVuZXNzLCB4bGFiPSJZZWFyIiwgeWxhYj0iTWVkaWFuIFZhbHVlIiwgeWxpbT1jKHNtYWxsZXN0LCBiaWdnZXN0KSwgbWFpbj0iTW9zdCBQb3B1bGFyIFNvbmcncyBMaXZlbmVzcyBGZWF0dXJlIFByZWRpY3Rpb24iLCBzdWI9cGFzdGUoIlByb2plY3RlZCBtZWRpYW4gZmVhdHVyZSB2YWx1ZSBmb3IgbW9zdCBwb3B1bGFyIHNvbmdzOiIsIHBhc3RlKHJvdW5kKGxpdmVuZXNzLnNhcmltYS5wcmVkaWN0JG1lYW4sMyksIGNvbGxhcHNlPSIsIikgKSkKbGluZXMoYmVzdF9saXZlbmVzcywgY29sID0gMikKbGluZXModHMobGl2ZW5lc3MucHJlZGljdFssMV0sIHN0YXJ0PTIwMjArMSwgZW5kPTIwMjArblRlc3QpLCBjb2wgPSAzKQpsZWdlbmQoInRvcHJpZ2h0IiwgbHR5PTEsIGNvbD1jKDEsMiwzKSwgbGVnZW5kPWMoIk92ZXJhbGwiLCAiTW9zdCBQb3B1bGFyIiwgIk1vc3QgUG9wdWxhciBQcm9qZWN0aW9uIikpCmBgYAoKCgojIyBMb3VkbmVzcwoKYGBge3J9CmJlc3QgPSBiZXN0X2xvdWRuZXNzCm92ZXJhbGwgPSBsb3VkbmVzcwoKbG91ZG5lc3MubG0gPC0gbG0oYmVzdCB+IG92ZXJhbGwpCnByZWRpY3RfZGYgPC0gZGF0YS5mcmFtZShvdmVyYWxsID0gbG91ZG5lc3Muc2VzJG1lYW4pCmxvdWRuZXNzLnByZWRpY3QgPC0gcHJlZGljdChsb3VkbmVzcy5sbSwgbmV3ZGF0YT1wcmVkaWN0X2RmLCBpbnRlcnZhbD0icHJlZGljdGlvbiIpCgpzbWFsbGVzdCA9IG1pbihtaW4oYmVzdCksIG1pbihvdmVyYWxsKSkKYmlnZ2VzdCA9IG1heChtYXgoYmVzdCksIG1heChvdmVyYWxsKSkKcGxvdChsb3VkbmVzcywgeGxhYj0iWWVhciIsIHlsYWI9Ik1lZGlhbiBWYWx1ZSIsIHlsaW09YyhzbWFsbGVzdCwgYmlnZ2VzdCksIG1haW49Ik1vc3QgUG9wdWxhciBTb25nJ3MgTG91ZG5lc3MgRmVhdHVyZSBQcmVkaWN0aW9uIiwgc3ViPXBhc3RlKCJQcm9qZWN0ZWQgbWVkaWFuIGZlYXR1cmUgdmFsdWUgZm9yIG1vc3QgcG9wdWxhciBzb25nczoiLCBwYXN0ZShyb3VuZChsb3VkbmVzcy5zZXMkbWVhbiwxKSwgY29sbGFwc2U9IiwiKSApKQpsaW5lcyhiZXN0X2xvdWRuZXNzLCBjb2wgPSAyKQpsaW5lcyh0cyhsb3VkbmVzcy5wcmVkaWN0WywxXSwgc3RhcnQ9MjAyMCsxLCBlbmQ9MjAyMCtuVGVzdCksIGNvbCA9IDMpCmxlZ2VuZCgiYm90dG9tcmlnaHQiLCBsdHk9MSwgY29sPWMoMSwyLDMpLCBsZWdlbmQ9YygiT3ZlcmFsbCIsICJNb3N0IFBvcHVsYXIiLCAiTW9zdCBQb3B1bGFyIFByb2plY3Rpb24iKSkKYGBgCiMjIyBJbXBsaWNhdGlvbnM6ClVzaW5nIHRoZXNlIHZhbHVlcyBhYm92ZSBmb3IgdGhlIHByZWRpY3RlZCBtZWRpYW4gdmFsdWUgZm9yIHRoZSBtb3N0IHBvcHVsYXIgc29uZ3MsIGZvciBlYWNoIGZlYXR1cmUsIG11c2ljIGN1cmF0b3JzIGFuZCByZWNvcmQgbGFiZWxzIGNhbiBwcmVkaWN0IHdoaWNoICJzb3VuZHMiIHdpbGwgYmUgbW9zdCBwb3B1bGFyIGluIHRoZSBmdXR1cmUuIFRoaXMgaW5mb3JtYXRpb24gY2FuIHRoZW4gYmUgdXNlZCBmb3IgZnV0dXJlIGRlY2lzaW9uIG1ha2luZyB3aXRoIHJlc3BlY3QgdG8gd2hpY2ggYXJ0aXN0cyB0byBzaWduIGFuZCB3aGljaCBzb3VuZHMgdG8gcHVzaC4=